import ast
import json
import re
from typing import Dict, List, Optional


def parse_xml_tool_call(xml_content: str) -> Optional[Dict]:
    params: Dict = {}
    func_match = re.search(r"<function=([^>]+)>", xml_content)
    if not func_match or func_match.group(1) != "computer_use":
        return None

    for match in re.finditer(r"<parameter=([^>]+)>\s*(.*?)\s*</parameter>", xml_content, re.DOTALL):
        name = match.group(1)
        value = match.group(2).strip()
        if value.startswith("[") or value.startswith("{"):
            try:
                params[name] = json.loads(value)
                continue
            except json.JSONDecodeError:
                pass
        params[name] = value
    return params


def iter_tool_call_params(response: str):
    for tool_call_match in re.finditer(r"<tool_call>(.*?)</tool_call>", response, re.DOTALL):
        params = parse_xml_tool_call(tool_call_match.group(1))
        if params:
            yield params


def parse_keys(raw_keys, *, lowercase: bool = False) -> List[str]:
    if isinstance(raw_keys, str):
        try:
            raw_keys = json.loads(raw_keys)
        except Exception:
            try:
                raw_keys = ast.literal_eval(raw_keys)
            except Exception:
                pass

    def clean_key_token(key: object) -> str:
        token = str(key).strip()
        token = token.strip(" \t\r\n[](){}\"'")
        token = token.rstrip(" \t\r\n]")
        token = token.lstrip(" \t\r\n[")
        return token.strip()

    def flatten(keys_obj) -> List[str]:
        if keys_obj is None:
            return []
        if isinstance(keys_obj, list):
            values: List[str] = []
            for item in keys_obj:
                values.extend(flatten(item))
            return values
        values = []
        for part in re.split(r"\s*\+\s*", str(keys_obj).strip()):
            cleaned = clean_key_token(part)
            if cleaned:
                values.append(cleaned)
        return values

    keys = flatten(raw_keys)
    return [key.lower() for key in keys] if lowercase else keys


def parse_coordinate(raw_coord):
    if isinstance(raw_coord, str):
        try:
            raw_coord = json.loads(raw_coord)
        except Exception:
            return None
    if isinstance(raw_coord, list) and len(raw_coord) >= 2:
        return raw_coord[0], raw_coord[1]
    return None


def parse_number(raw_value, default=0.0) -> float:
    try:
        return float(raw_value)
    except Exception:
        return float(default)


def extract_action_line(response: str, *, preserve_base_split_bug: bool = False) -> str:
    for line in response.split("\n"):
        stripped = line.strip()
        if stripped.lower().startswith("action:"):
            if preserve_base_split_bug:
                return stripped.split("Action:", 1)[-1].strip()
            return stripped.split(":", 1)[-1].strip()
    return ""


def looks_infeasible_response(text: str) -> bool:
    lowered = text.lower()
    if "[infeasible]" in lowered or "infeasible" in lowered:
        return True

    literal_patterns = [
        "not possible",
        "impossible",
        "not feasible",
        "cannot be completed",
        "can't be completed",
        "cannot be done",
        "cannot complete",
        "can't complete",
        "unable to complete",
        "cannot do this task",
        "can't do this task",
        "cannot complete this task as described",
        "cannot be completed as specified",
        "can't be completed as specified",
        "not available in your country",
        "not available",
        "unavailable",
        "not supported",
        "does not support",
        "doesn't support",
        "cannot natively",
        "does not have a built-in",
        "doesn't have a built-in",
        "does not include",
        "is not among the natively built-in",
        "will fall back to english",
        "requires the official",
        "no bluetooth found",
        "plug in a dongle",
        "folder is empty",
        "downloads folder is empty",
        "do not have the credentials",
        "don't have the credentials",
        "do not have the account credentials",
        "don't have the account credentials",
        "need the user's google account credentials",
        "requires a language pack extension",
        "requires email verification",
        "requires a sign-up",
        "requires sign-up",
        "requires google account credentials",
        "requires a google account",
        "sign in to the google account",
        "drm-protected",
        "drm protection",
        "cannot directly play",
        "no legitimate way",
        "requires a plugin",
        "requires an extension",
        "requires extension",
        "requires plugin",
        "requires a valid account",
        "requires purchase",
        "requires a purchased",
        "no valid account",
        "hidden audio",
        "could you clarify",
    ]
    if any(pattern in lowered for pattern in literal_patterns):
        return True

    regex_patterns = [
        r"\bthere is no [a-z0-9 _-]+\b",
        r"\bno [a-z0-9 _-]+ in [a-z0-9 _-]+ list\b",
        r"\brequires? (an? )?(extension|plugin|account|credentials|hardware|language pack)\b",
        r"\bneed(?:s)? (an? )?(extension|plugin|account|credentials|hardware|language pack)\b",
        r"\b(without|no) (extensions?|plugins?|terminal|ffmpeg|other apps?).{0,120}\\b(cannot|can't|not possible|not feasible)\b",
    ]
    return any(re.search(pattern, lowered) for pattern in regex_patterns)
