"""Generate standalone nodriver Python replay scripts from action lists."""

from __future__ import annotations

import json
from datetime import datetime
from typing import Any

from .locator import normalize_action_for_replay


def append_press_key_code(lines: list[str], key: str) -> None:
    """Append nodriver CDP code for a press_key action."""
    from ..tools._keys import parse_key as _parse  # pylint: disable=import-outside-toplevel

    actual_key, vk, modifiers = _parse(key)

    lines.append("    from nodriver import cdp")
    lines.append("    await tab.send(cdp.input_.dispatch_key_event(")
    lines.append(
        f"        \"rawKeyDown\", windows_virtual_key_code={vk}, modifiers={modifiers}, key={actual_key!r}",
    )
    lines.append("    ))")
    lines.append("    await tab.send(cdp.input_.dispatch_key_event(")
    lines.append(
        f"        \"keyUp\", windows_virtual_key_code={vk}, modifiers={modifiers}, key={actual_key!r}",
    )
    lines.append("    ))")


def generate_nodriver_script(
    actions: list[dict[str, Any]],
    cdp_port: int,
    start_url: str,
    name: str = "",
    *,
    session_id: str = "",
    backend_type: str = "",
    store_name: str = "",
) -> str:
    """Convert JSON action list to a standalone nodriver Python script."""
    now_str = datetime.now().strftime("%Y-%m-%d %H:%M")
    title = name or "recording"
    sid = session_id or "—"
    bt = backend_type or "—"
    sn = store_name or "—"
    doc = f'''"""Auto-generated by ziniao-mcp recorder - {now_str} recording: {title}

Recording context (human-readable; this script only uses CDP via nodriver):
  session_id: {sid}
  backend_type: {bt}
  store_name: {sn}
  cdp_port: {cdp_port}
  start_url: {start_url or "(empty → about:blank at replay)"}

Requires the browser CDP endpoint to be listening on 127.0.0.1 (--port).
For Ziniao, open the store first, e.g. ``ziniao open-store <session_id>``,
then run: ``python {title}.py`` (or pass ``--port`` if different).
"""'''

    lines: list[str] = []
    lines.append(doc)
    lines.append("")
    lines.append("import argparse")
    lines.append("import asyncio")
    lines.append("import random")
    lines.append("")
    lines.append("import nodriver")
    lines.append("from nodriver import cdp")
    lines.append("")
    lines.append("")
    lines.append("async def main(port: int) -> None:")
    lines.append('    browser = await nodriver.Browser.create(host="127.0.0.1", port=port)')
    replay_target = start_url or "about:blank"
    lines.append(f"    tab = await browser.get({replay_target!r}, new_tab=True)")
    lines.append("    await tab.sleep(1)")
    lines.append("")

    step = 0

    for act in actions:
        act = normalize_action_for_replay(act)
        step += 1
        act_type = act.get("type", "")
        dm = int(act.get("delay_ms", 0) or 0)
        if dm > 100 and step > 1:
            lines.append(f"    await tab.sleep({round(dm / 1000, 2)})")

        if act_type == "click":
            sel = act.get("selector") or "body"
            lines.append(f"    # {step}. Click {sel}")
            lines.append(f"    elem = await tab.select({sel!r}, timeout=10)")
            lines.append("    if elem:")
            lines.append("        pos = await elem.get_position()")
            lines.append("        if pos:")
            lines.append("            cx, cy = pos.center")
            lines.append("            await tab.mouse_click(cx, cy)")
            lines.append("        else:")
            lines.append("            await elem.mouse_click()")

        elif act_type == "fill":
            sel = act.get("selector") or "body"
            val = act.get("value", "")
            lines.append(f"    # {step}. Fill {sel}")
            lines.append(f"    elem = await tab.select({sel!r}, timeout=10)")
            lines.append("    if elem:")
            lines.append("        pos = await elem.get_position()")
            lines.append("        if pos:")
            lines.append("            await tab.mouse_click(*pos.center)")
            lines.append("        else:")
            lines.append("            await elem.mouse_click()")
            lines.append("        await asyncio.sleep(random.uniform(0.1, 0.3))")
            lines.append(
                "        await tab.send(cdp.input_.dispatch_key_event("
                "'rawKeyDown', windows_virtual_key_code=65, modifiers=2))",
            )
            lines.append(
                "        await tab.send(cdp.input_.dispatch_key_event("
                "'keyUp', windows_virtual_key_code=65, modifiers=2))",
            )
            lines.append("        await asyncio.sleep(0.05)")
            lines.append(
                "        await tab.send(cdp.input_.dispatch_key_event("
                "'rawKeyDown', windows_virtual_key_code=8))",
            )
            lines.append(
                "        await tab.send(cdp.input_.dispatch_key_event('keyUp', windows_virtual_key_code=8))",
            )
            lines.append(f"        for char in {val!r}:")
            lines.append(
                "            await tab.send(cdp.input_.dispatch_key_event('char', text=char))",
            )
            lines.append("            await asyncio.sleep(random.uniform(0.05, 0.15))")

        elif act_type == "select":
            sel = act.get("selector") or "select"
            val = act.get("value", "")
            lines.append(f"    # {step}. Select {sel} = {val}")
            js_code = (
                f"document.querySelector({json.dumps(sel)}).value = {json.dumps(val)}; "
                f"document.querySelector({json.dumps(sel)}).dispatchEvent(new Event('change'))"
            )
            lines.append(f"    await tab.evaluate({js_code!r})")

        elif act_type == "press_key":
            key = act.get("key", "Enter")
            lines.append(f"    # {step}. Press key {key}")
            append_press_key_code(lines, key)

        elif act_type == "navigate":
            url = act.get("url", "")
            lines.append(f"    # {step}. Navigate to {url}")
            lines.append(f"    await tab.get({url!r})")
            lines.append("    await tab.sleep(1)")

        else:
            lines.append(f"    # {step}. Unknown action: {act_type}")

        lines.append("")

    lines.append("")
    lines.append('if __name__ == "__main__":')
    lines.append('    parser = argparse.ArgumentParser(description="Replay a ziniao-mcp recording")')
    lines.append(f'    parser.add_argument("--port", type=int, default={cdp_port}, help="CDP port")')
    lines.append("    args = parser.parse_args()")
    lines.append("    asyncio.run(main(args.port))")
    lines.append("")

    return "\n".join(lines)
