from __future__ import annotations

import argparse
import shutil
import sqlite3
import subprocess
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
FRONTEND_ROOT = ROOT / "frontend"
E2E_URL = "http://127.0.0.1:8876/index.html"


def run(label: str, command: list[str]) -> None:
    print(f"\n[{label}] {' '.join(command)}", flush=True)
    subprocess.run(command, cwd=ROOT, check=True)


def python_test_command(preservation_baseline: bool | None = None) -> list[str]:
    if preservation_baseline is None:
        preservation_baseline = (ROOT.parent / "static" / "app.js").is_file()
    if preservation_baseline:
        return [sys.executable, "-m", "unittest", "discover", "-s", "tests"]
    modules = [
        f"tests.{path.stem}"
        for path in sorted((ROOT / "tests").glob("test_*.py"))
        if not path.stem.startswith("test_preservation_")
    ]
    if not modules:
        raise RuntimeError("no standalone candidate tests found")
    return [sys.executable, "-m", "unittest", *modules]


def verify_git_diff() -> None:
    result = subprocess.run(
        ["git", "rev-parse", "--show-toplevel"],
        cwd=ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    if result.returncode != 0:
        print("\n[patch] skipped: standalone export is not a Git checkout")
        return
    repository_root = Path(result.stdout.strip()).resolve()
    if ROOT != repository_root / "app":
        print("\n[patch] skipped: standalone export is outside the canonical app path")
        return
    run("patch", ["git", "diff", "--check"])


def verify_database() -> None:
    database = ROOT / "data" / "review.db"
    if not database.exists():
        print("\n[database] skipped: data/review.db does not exist")
        return
    with sqlite3.connect(f"file:{database.as_posix()}?mode=ro", uri=True) as connection:
        result = connection.execute("PRAGMA integrity_check").fetchone()
    if not result or result[0] != "ok":
        raise RuntimeError(f"SQLite integrity check failed: {result}")
    print(f"\n[database] integrity_check=ok size={database.stat().st_size}")


def url_reachable(url: str) -> bool:
    try:
        with urllib.request.urlopen(url, timeout=1) as response:
            return response.status == 200
    except (OSError, urllib.error.URLError):
        return False


def start_e2e_server() -> subprocess.Popen[bytes] | None:
    if url_reachable(E2E_URL):
        print(f"\n[e2e-server] reusing {E2E_URL}")
        return None
    creation_flags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0
    process = subprocess.Popen(
        [
            sys.executable,
            "-m",
            "http.server",
            "8876",
            "--bind",
            "127.0.0.1",
            "--directory",
            "frontend",
        ],
        cwd=ROOT,
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        creationflags=creation_flags,
    )
    deadline = time.monotonic() + 15
    while time.monotonic() < deadline:
        if process.poll() is not None:
            raise RuntimeError("Playwright static server exited before becoming ready")
        if url_reachable(E2E_URL):
            print(f"\n[e2e-server] started {E2E_URL}")
            return process
        time.sleep(0.1)
    stop_e2e_server(process)
    raise RuntimeError("Playwright static server did not become ready within 15 seconds")


def stop_e2e_server(process: subprocess.Popen[bytes] | None) -> None:
    if process is None:
        return
    process.terminate()
    try:
        process.wait(timeout=5)
    except subprocess.TimeoutExpired:
        process.kill()
        process.wait(timeout=5)
    print("\n[e2e-server] stopped")


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Verify the modular preservation candidate"
    )
    parser.add_argument(
        "--e2e",
        action="store_true",
        help="also run the Playwright browser suite",
    )
    args = parser.parse_args()

    run("python", python_test_command())
    run(
        "api-registry",
        [sys.executable, "tools/build_api_registry.py", "--check"],
    )
    run(
        "architecture-inventory",
        [sys.executable, "tools/build_architecture_inventory.py", "--check"],
    )
    node = shutil.which("node")
    if not node:
        raise RuntimeError("node is required for JavaScript syntax checks")
    scripts = sorted(FRONTEND_ROOT.rglob("*.js"))
    if not scripts:
        raise RuntimeError(f"no JavaScript files found under {FRONTEND_ROOT}")
    for script in scripts:
        run(
            "javascript",
            [node, "--check", script.relative_to(ROOT).as_posix()],
        )
    verify_git_diff()
    verify_database()

    if args.e2e:
        npx = shutil.which("npx.cmd" if sys.platform == "win32" else "npx")
        if not npx:
            raise RuntimeError("npx is required for the Playwright suite")
        server = start_e2e_server()
        try:
            run("playwright", [npx, "playwright", "test", "--reporter=dot"])
        finally:
            stop_e2e_server(server)
    print("\nBaseline verification passed.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
