#!/usr/bin/env python3
from __future__ import annotations

import argparse, re, sys
from pathlib import Path

CATALOG = Path("scenarios/catalog.yml")
JAVA = Path("stacks/java-selenium-testng/src/test/java/theinternetwebsite/ui/testcases")
TS = Path("stacks/ts-playwright/tests")
PY = Path("stacks/python-playwright/tests")
MATRIX = Path("docs/scenario-matrix.md")
STACKS = ("java-selenium-testng", "ts-playwright", "python-playwright")
SCENARIO_ID = r"(?:UI|HTTP)-[A-Z0-9-]+"

def catalog() -> list[dict[str, object]]:
    rows: list[dict[str, object]] = []
    for line in CATALOG.read_text().splitlines():
        s = line.strip()
        if s.startswith("- id: "):
            rows.append({"id": s[6:]})
        elif rows and s.startswith(("title: ", "priority: ")):
            key, val = s.split(": ", 1); rows[-1][key] = val
        elif rows and s.startswith("coverage: {"):
            rows[-1]["coverage"] = dict(re.findall(r"([\w-]+): (true|false)", s))
    return rows

def java_ids() -> set[str]:
    text = "\n".join(p.read_text() for p in JAVA.glob("*.java"))
    return set(re.findall(rf'testName\s*=\s*"({SCENARIO_ID})"', text))

def ts_ids() -> set[str]:
    text = "\n".join(p.read_text() for p in TS.glob("**/*.ts")) if TS.exists() else ""
    return set(re.findall(rf"\b({SCENARIO_ID})\b", text))

def py_ids() -> set[str]:
    text = "\n".join(p.read_text() for p in PY.glob("**/*.py")) if PY.exists() else ""
    return set(re.findall(rf"\b({SCENARIO_ID})\b", text))

def check(rows: list[dict[str, object]]) -> None:
    ids = [str(r["id"]) for r in rows]; all_ids = set(ids); java_tests = java_ids(); ts_tests = ts_ids(); py_tests = py_ids()
    java_catalog = {str(r["id"]) for r in rows if dict(r["coverage"])["java-selenium-testng"] == "true"}
    ts_catalog = {str(r["id"]) for r in rows if dict(r["coverage"])["ts-playwright"] == "true"}
    py_catalog = {str(r["id"]) for r in rows if dict(r["coverage"])["python-playwright"] == "true"}
    errors = []
    errors += [f"duplicate catalog ids: {sorted({i for i in ids if ids.count(i) > 1})}"] if len(ids) != len(all_ids) else []
    errors += [f"catalog java ids missing tests: {sorted(java_catalog - java_tests)}"] if java_catalog - java_tests else []
    errors += [f"java test ids missing catalog rows: {sorted(java_tests - all_ids)}"] if java_tests - all_ids else []
    errors += [f"java test ids not marked covered: {sorted(java_tests - java_catalog)}"] if java_tests - java_catalog else []
    errors += [f"catalog ts ids missing tests: {sorted(ts_catalog - ts_tests)}"] if ts_catalog - ts_tests else []
    errors += [f"ts test ids missing catalog rows: {sorted(ts_tests - all_ids)}"] if ts_tests - all_ids else []
    errors += [f"ts test ids not marked covered: {sorted(ts_tests - ts_catalog)}"] if ts_tests - ts_catalog else []
    errors += [f"catalog py ids missing tests: {sorted(py_catalog - py_tests)}"] if py_catalog - py_tests else []
    errors += [f"py test ids missing catalog rows: {sorted(py_tests - all_ids)}"] if py_tests - all_ids else []
    errors += [f"py test ids not marked covered: {sorted(py_tests - py_catalog)}"] if py_tests - py_catalog else []
    if errors: sys.exit("\n".join(errors))

def write_matrix(rows: list[dict[str, object]]) -> None:
    out = ["# Scenario coverage matrix", "", "Generated by `tools/check-scenarios.py --write-matrix`.", "", "| ID | Priority | Scenario | Java | TS | Python |", "| --- | --- | --- | --- | --- | --- |"]
    for r in rows:
        c = dict(r["coverage"]); marks = ["✅" if c[s] == "true" else "—" for s in STACKS]
        out.append(f"| {r['id']} | {r['priority']} | {r['title']} | {marks[0]} | {marks[1]} | {marks[2]} |")
    MATRIX.parent.mkdir(exist_ok=True); MATRIX.write_text("\n".join(out) + "\n")

parser = argparse.ArgumentParser(); parser.add_argument("--write-matrix", action="store_true")
args = parser.parse_args(); rows = catalog(); check(rows)
if args.write_matrix: write_matrix(rows)
print(f"scenario catalog ok: {len(rows)} rows, {len(java_ids())} java tests, {len(ts_ids())} ts tests, {len(py_ids())} py tests")
