"""Modernise Playwright locator patterns to the recommended accessible-name API."""
from __future__ import annotations
import re

# ---------------------------------------------------------------------------
# Patterns that map cleanly to a modern Playwright locator method
# ---------------------------------------------------------------------------

# page.locator('[data-testid="x"]')  →  page.getByTestId('x')
_TESTID_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\[data-testid=['\"]([^'\"]+)['\"]\]['\"]"""
    r"""\s*\)""",
    re.DOTALL,
)

# page.locator('text=Foo')  →  page.getByText('Foo')
_TEXT_EQ_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\s*text\s*=\s*([^'\"]+?)\s*['\"]"""
    r"""\s*\)"""
)

# page.locator(':text("Foo")') / page.locator(':text-is("Foo")')
_TEXT_PSEUDO_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\s*:text(?:-is)?\(['\"](.*?)['\"]\)"""
    r"""['\"]"""
    r"""\s*\)"""
)

# page.locator('[placeholder="Foo"]')
_PLACEHOLDER_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\[placeholder=['\"]([^'\"]+)['\"]\]['\"]"""
    r"""\s*\)"""
)

# page.locator('[aria-label="Foo"]')
_ARIA_LABEL_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\[aria-label=['\"]([^'\"]+)['\"]\]['\"]"""
    r"""\s*\)"""
)

# page.locator('role=button[name="Submit"]')
_ROLE_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\s*role\s*=\s*(\w+)\[name=['\"]([^'\"]+)['\"]\]['\"]"""
    r"""\s*\)"""
)

# ---------------------------------------------------------------------------
# Patterns that are too complex for auto-conversion — flag with TODO
# ---------------------------------------------------------------------------

_XPATH_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*"""
    r"""(?:'[^']*(?:xpath=)?//[^']*'|"[^"]*(?:xpath=)?//[^"]*")"""
    r"""\s*\)"""
)

_COMPLEX_CSS_RE = re.compile(
    r"""(?:page|this\.page)\s*\.\s*locator\(\s*['\"]\s*[^'\"]*"""
    r"""(?:\s+>\s+|\s+\+\s+|\s+~\s+|:{1,2}[\w-]+\(|nth-child|nth-of-type)[^'\"]*['\"]"""
    r"""\s*\)"""
)

_TODO_COMMENT = "/* TODO: convert to getByRole when accessible name is known */"


def modernise_locators(content: str) -> tuple[str, list[str], int]:
    """
    Modernise Playwright locator patterns.
    Returns (transformed_content, changes, todo_count).
    """
    changes: list[str] = []
    todo_count = 0

    def _sub_testid(m: re.Match) -> str:
        tid = m.group(1)
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f"[data-testid='{tid}'] → getByTestId('{tid}')")
        return f"{prefix}.getByTestId('{tid}')"

    def _sub_text_eq(m: re.Match) -> str:
        text = m.group(1).strip()
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f"text={text!r} → getByText('{text}')")
        return f"{prefix}.getByText('{text}')"

    def _sub_text_pseudo(m: re.Match) -> str:
        text = m.group(1)
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f":text('{text}') → getByText('{text}')")
        return f"{prefix}.getByText('{text}')"

    def _sub_placeholder(m: re.Match) -> str:
        ph = m.group(1)
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f"[placeholder='{ph}'] → getByPlaceholder('{ph}')")
        return f"{prefix}.getByPlaceholder('{ph}')"

    def _sub_aria_label(m: re.Match) -> str:
        lbl = m.group(1)
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f"[aria-label='{lbl}'] → getByLabel('{lbl}')")
        return f"{prefix}.getByLabel('{lbl}')"

    def _sub_role(m: re.Match) -> str:
        role, name = m.group(1), m.group(2)
        prefix = "this.page" if "this.page" in m.group(0) else "page"
        changes.append(f"role={role}[name='{name}'] → getByRole('{role}', {{ name: '{name}' }})")
        return f"{prefix}.getByRole('{role}', {{ name: '{name}' }})"

    def _flag_xpath(m: re.Match) -> str:
        nonlocal todo_count
        todo_count += 1
        return f"{_TODO_COMMENT} {m.group(0)}"

    def _flag_complex(m: re.Match) -> str:
        nonlocal todo_count
        original = m.group(0)
        if _TODO_COMMENT in original:
            return original
        todo_count += 1
        return f"{_TODO_COMMENT} {original}"

    content = _TESTID_RE.sub(_sub_testid, content)
    content = _TEXT_EQ_RE.sub(_sub_text_eq, content)
    content = _TEXT_PSEUDO_RE.sub(_sub_text_pseudo, content)
    content = _PLACEHOLDER_RE.sub(_sub_placeholder, content)
    content = _ARIA_LABEL_RE.sub(_sub_aria_label, content)
    content = _ROLE_RE.sub(_sub_role, content)
    content = _XPATH_RE.sub(_flag_xpath, content)
    content = _COMPLEX_CSS_RE.sub(_flag_complex, content)

    return content, changes, todo_count
