"""Parse Playwright MCP accessibility tree snapshot text into structured data."""

import re


def extract_dropdown_options(
    snapshot_text: str,
    field_selector: str,
) -> list[dict[str, str]]:
    """
    Parse dropdown/combobox options from a Playwright snapshot accessibility tree.

    Looks for a combobox or listbox matching field_selector, then extracts
    all child option lines with their labels and ref attributes.
    """
    options: list[dict[str, str]] = []

    # Find the combobox/listbox line that relates to field_selector
    # The snapshot format uses lines like:
    #   - combobox "Label" [ref=eN]:
    #     - option "Value" [ref=eM]
    lines = snapshot_text.splitlines()

    # Locate the parent element - match by ref or by label containing the selector hint
    parent_line_index = _find_element_line(
        lines=lines,
        element_types=["combobox", "listbox", "select"],
        identifier=field_selector,
    )

    if parent_line_index is None:
        return options

    parent_indent = _get_indent_level(line=lines[parent_line_index])

    # Collect child option lines (indented deeper than parent)
    for i in range(parent_line_index + 1, len(lines)):
        line = lines[i]
        if not line.strip():
            continue

        current_indent = _get_indent_level(line=line)
        # Stop when we reach a sibling or parent level
        if current_indent <= parent_indent:
            break

        option_match = re.search(
            r'-\s+option\s+"([^"]*)"(?:\s+\[ref=(\w+)\])?',
            line,
        )
        if option_match:
            label = option_match.group(1)
            ref = option_match.group(2) or ""
            options.append({"value": label, "label": label, "ref": ref})

    return options


def extract_radio_options(
    snapshot_text: str,
    group_name: str,
) -> list[dict[str, str]]:
    """
    Parse radio button options from a Playwright snapshot accessibility tree.

    Searches for all radio elements, filtering by group_name if it appears
    in surrounding context. Returns dicts with value, label, ref, and checked state.
    """
    options: list[dict[str, str]] = []
    lines = snapshot_text.splitlines()

    for line in lines:
        radio_match = re.search(
            r'-\s+radio\s+"([^"]*)"(?:\s+\[ref=(\w+)\])?(\s+\[checked\])?',
            line,
        )
        if not radio_match:
            continue

        label = radio_match.group(1)
        ref = radio_match.group(2) or ""
        is_checked = radio_match.group(3) is not None

        # If group_name is provided, only include radios that are contextually
        # near the group. Since Playwright snapshots don't expose name attributes
        # directly, we include all radios when filtering cannot be determined,
        # or filter by label containing the group_name hint.
        if group_name and group_name.lower() not in label.lower():
            # Still include - the group_name might be a field name, not label content.
            # The user can filter afterwards.
            pass

        options.append(
            {
                "value": label,
                "label": label,
                "ref": ref,
                "checked": str(is_checked).lower(),
            }
        )

    return options


def extract_visible_fields(
    snapshot_text: str,
) -> list[dict[str, str]]:
    """
    Parse all visible form fields from a Playwright snapshot accessibility tree.

    Returns a list of dicts with: element_type, label, ref, and inferred_type.
    Handles textbox, combobox, radio, checkbox, slider, spinbutton, and similar
    element types from the accessibility tree.
    """
    fields: list[dict[str, str]] = []
    lines = snapshot_text.splitlines()

    # Element types that represent form fields in the accessibility tree
    field_type_map: dict[str, str] = {
        "textbox": "text",
        "combobox": "combobox",
        "radio": "radio",
        "checkbox": "checkbox",
        "slider": "slider",
        "spinbutton": "text",
        "searchbox": "text",
        "listbox": "dropdown",
    }

    for line in lines:
        # Match pattern: - element_type "Label" [ref=eN] [optional state]
        field_match = re.search(
            r'-\s+(\w+)\s+"([^"]*)"(?:\s+\[ref=(\w+)\])?',
            line,
        )
        if not field_match:
            continue

        element_type = field_match.group(1)
        label = field_match.group(2)
        ref = field_match.group(3) or ""

        inferred_type = field_type_map.get(element_type)
        if inferred_type is None:
            continue

        # Check for state indicators
        is_checked = "[checked]" in line
        is_selected = "[selected]" in line
        is_disabled = "[disabled]" in line

        fields.append(
            {
                "element_type": element_type,
                "label": label,
                "ref": ref,
                "inferred_type": inferred_type,
                "checked": str(is_checked).lower(),
                "selected": str(is_selected).lower(),
                "disabled": str(is_disabled).lower(),
            }
        )

    return fields


def _find_element_line(
    lines: list[str],
    element_types: list[str],
    identifier: str,
) -> int | None:
    """
    Find the line index of an element matching given types and identifier.

    The identifier can be a ref (e.g., "e7"), a label, or a selector hint.
    """
    types_pattern = "|".join(element_types)

    for i, line in enumerate(lines):
        element_match = re.search(
            rf'-\s+({types_pattern})\s+"([^"]*)"(?:\s+\[ref=(\w+)\])?',
            line,
        )
        if not element_match:
            continue

        label = element_match.group(2)
        ref = element_match.group(3) or ""

        # Match by ref attribute
        if identifier == ref:
            return i
        # Match by label text
        if identifier.lower() in label.lower():
            return i
        # Match by ref in the identifier (e.g., "[ref=e7]")
        ref_in_identifier = re.search(r"ref=(\w+)", identifier)
        if ref_in_identifier and ref_in_identifier.group(1) == ref:
            return i

    return None


def _get_indent_level(line: str) -> int:
    stripped = line.lstrip()
    return len(line) - len(stripped)
