from __future__ import annotations

import json
import sys
import time
from typing import Any

from playwright.sync_api import TimeoutError as PlaywrightTimeoutError
from playwright.sync_api import sync_playwright


BASE_URL = "https://mod-cms-onboarding.dry-salad-ffa1.workers.dev"
INSTALL_URL = f"{BASE_URL}/install"
LOGIN_URL = f"{BASE_URL}/admin/login"
ADMIN_EMAIL = "owner-verify2@example.com"
ADMIN_PASSWORD = "Verify2!StrongPassphrase#2026"
SITE_TITLE = f"Verification Site {int(time.time())}"
TAGLINE = "Live deploy verification run"


def text_of(locator: Any) -> str:
    try:
        return (locator.inner_text(timeout=2000) or "").strip()
    except Exception:
        return ""


def maybe_fill(page: Any, selectors: list[str], value: str) -> bool:
    for selector in selectors:
        loc = page.locator(selector).first
        try:
            if loc.is_visible(timeout=1500):
                loc.fill(value)
                return True
        except Exception:
            continue
    return False


def click_continue(page: Any) -> str:
    for name in ["Continue", "Finish setup"]:
        btn = page.get_by_role("button", name=name).first
        try:
            if btn.is_visible(timeout=1500):
                btn.click()
                return name
        except Exception:
            continue
    raise RuntimeError("Primary onboarding button not found")


def visible_headings(page: Any) -> list[str]:
    items: list[str] = []
    for role in ["heading", "button", "textbox"]:
        try:
            count = min(page.get_by_role(role).count(), 10)
            for i in range(count):
                txt = text_of(page.get_by_role(role).nth(i))
                if txt:
                    items.append(f"{role}:{txt}")
        except Exception:
            pass
    return items


def wait_idle(page: Any) -> None:
    try:
        page.wait_for_load_state("networkidle", timeout=10000)
    except Exception:
        pass


def main() -> int:
    report: dict[str, Any] = {
        "onboarding": {"status": "FAIL", "detail": ""},
        "mouse_click_login": {"status": "FAIL", "detail": ""},
        "console_messages": [],
        "page_errors": [],
        "failed_requests": [],
        "final_dashboard_state": "",
        "stopped_at_step": "",
    }

    try:
        with sync_playwright() as p:
            browser = p.chromium.launch(headless=True, args=["--no-sandbox"])
            context = browser.new_context()
            page = context.new_page()

            def on_console(msg: Any) -> None:
                report["console_messages"].append(f"[{msg.type}] {msg.text}")

            def on_pageerror(err: Any) -> None:
                report["page_errors"].append(str(err))

            def on_response(resp: Any) -> None:
                try:
                    if resp.status >= 400:
                        report["failed_requests"].append(f"{resp.status} {resp.request.method} {resp.url}")
                except Exception:
                    pass

            page.on("console", on_console)
            page.on("pageerror", on_pageerror)
            page.on("response", on_response)

            report["stopped_at_step"] = "navigate_install"
            page.goto(INSTALL_URL, wait_until="domcontentloaded", timeout=30000)
            wait_idle(page)

            for step_index in range(1, 12):
                report["stopped_at_step"] = f"onboarding_step_{step_index}"
                url = page.url
                if "/admin" in url and "/install" not in url:
                    report["onboarding"] = {"status": "PASS", "detail": f"Completed and landed on {url}"}
                    break

                maybe_fill(page, ['input[name="siteTitle"]', 'input[placeholder*="Site name" i]'], SITE_TITLE)
                maybe_fill(page, ['input[name="tagline"]', 'input[placeholder*="Tagline" i]'], TAGLINE)
                maybe_fill(page, ['input[name="adminEmail"]', 'input[type="email"]'], ADMIN_EMAIL)
                maybe_fill(page, ['input[name="adminPassword"]', 'input[type="password"]'], ADMIN_PASSWORD)

                before = page.url
                clicked = click_continue(page)
                try:
                    page.wait_for_load_state("domcontentloaded", timeout=10000)
                except Exception:
                    pass
                wait_idle(page)
                time.sleep(0.5)

                if any("Setup already in progress" in m for m in report["console_messages"]):
                    raise RuntimeError("Saw 'Setup already in progress' in console")

                body_text = ""
                try:
                    body_text = (page.locator("body").inner_text(timeout=2000) or "").strip()
                except Exception:
                    pass
                if "Setup already in progress" in body_text:
                    raise RuntimeError("Page showed 'Setup already in progress'")

                if clicked == "Finish setup" and page.url == before and "/install" in page.url:
                    raise RuntimeError(
                        f"Clicked Finish setup but remained on install page. Visible elements: {visible_headings(page)}"
                    )

            if report["onboarding"]["status"] != "PASS":
                raise RuntimeError(f"Onboarding did not complete. Current URL: {page.url}. Visible: {visible_headings(page)}")

            login_page = context.new_page()
            login_page.on("console", on_console)
            login_page.on("pageerror", on_pageerror)
            login_page.on("response", on_response)

            report["stopped_at_step"] = "navigate_login"
            login_page.goto(LOGIN_URL, wait_until="domcontentloaded", timeout=30000)
            wait_idle(login_page)

            report["stopped_at_step"] = "submit_login_by_mouse"
            email_filled = maybe_fill(login_page, ['input[name="email"]', 'input[type="email"]', 'input[name="adminEmail"]'], ADMIN_EMAIL)
            password_filled = maybe_fill(login_page, ['input[name="password"]', 'input[type="password"]', 'input[name="adminPassword"]'], ADMIN_PASSWORD)
            if not email_filled or not password_filled:
                raise RuntimeError(f"Could not find login inputs. Visible: {visible_headings(login_page)}")

            sign_in = login_page.get_by_role("button", name="Sign in").first
            if not sign_in.is_visible(timeout=5000):
                raise RuntimeError("Sign in button not visible")

            sign_in.click()
            login_page.wait_for_load_state("domcontentloaded", timeout=10000)
            wait_idle(login_page)
            time.sleep(0.75)

            if "/admin/login" in login_page.url:
                body_text = ""
                try:
                    body_text = (login_page.locator("body").inner_text(timeout=2000) or "").strip()
                except Exception:
                    pass
                raise RuntimeError(f"Mouse click login stayed on login page. URL: {login_page.url}. Body excerpt: {body_text[:500]}")

            report["mouse_click_login"] = {"status": "PASS", "detail": f"Mouse click submitted and navigated to {login_page.url}"}

            dashboard_text = ""
            try:
                dashboard_text = (login_page.locator("body").inner_text(timeout=5000) or "").strip()
            except Exception:
                pass
            report["final_dashboard_state"] = f"URL={login_page.url}; body_excerpt={dashboard_text[:200]}"

            browser.close()
    except PlaywrightTimeoutError as exc:
        report["stopped_at_step"] = report.get("stopped_at_step") or "unknown"
        if not report["onboarding"]["detail"]:
            report["onboarding"]["detail"] = str(exc)
        report["error"] = f"Timeout: {exc}"
    except Exception as exc:
        report["error"] = str(exc)

    print(json.dumps(report, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    sys.exit(main())
