#!/usr/bin/env python3
"""
run_tests.py — local test runner for COMP3311 Ass2 (ass2.py).

Compares your ass2.py output against pre-recorded golden outputs.
No solution script or special setup required — just a running PostgreSQL
with the mymyunsw database loaded.

Usage:
    python3 tests/run_tests.py                     # test all
    python3 tests/run_tests.py --only=q3           # filter by name substring
    python3 tests/run_tests.py path/to/my_ass2.py  # test a specific file
"""

from __future__ import annotations

import argparse
import difflib
import json
import subprocess
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parent
ROOT = HERE.parent
GOLDEN_FILE = HERE / "output.json"
DB_DUMP = ROOT / "mymyunsw.dump"
DEFAULT_STUDENT = ROOT / "ass2.py"


def reset_db() -> None:
    """Reload mymyunsw from dump to restore clean state."""
    r = subprocess.run(
        ["psql", "mymyunsw", "-f", str(DB_DUMP)],
        capture_output=True, text=True,
    )
    if r.returncode != 0:
        print("ERROR: failed to reload mymyunsw from dump.", file=sys.stderr)
        print("Make sure PostgreSQL is running and 'mymyunsw' exists.", file=sys.stderr)
        print("If missing:  createdb mymyunsw && psql mymyunsw -f mymyunsw.dump",
              file=sys.stderr)
        sys.exit(1)


def run_script(script: Path, stdin: str) -> str:
    """Run a Python script with given stdin; return stdout."""
    r = subprocess.run(
        [sys.executable, str(script)],
        input=stdin, capture_output=True, text=True,
    )
    if r.returncode != 0:
        print(f"ERROR: {script.name} exited with code {r.returncode}", file=sys.stderr)
        if r.stderr:
            print(r.stderr[:500], file=sys.stderr)
    return r.stdout.replace("\r\n", "\n")


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Test your ass2.py against golden outputs (no solution needed).",
    )
    parser.add_argument(
        "student_file", nargs="?", default=str(DEFAULT_STUDENT),
        help=f"Path to your ass2.py (default: {DEFAULT_STUDENT})",
    )
    parser.add_argument(
        "--only", dest="only",
        help="Run only scenarios whose name contains this substring (e.g. --only=q3)",
    )
    args = parser.parse_args()

    script = Path(args.student_file).resolve()
    if not script.is_file():
        print(f"Error: file not found: {script}", file=sys.stderr)
        sys.exit(1)

    if not GOLDEN_FILE.is_file():
        print(f"Error: golden file not found: {GOLDEN_FILE}", file=sys.stderr)
        sys.exit(1)

    with open(GOLDEN_FILE, encoding="utf-8") as f:
        entries = json.load(f)

    if args.only:
        entries = [e for e in entries if args.only in e["name"]]
        if not entries:
            print(f"No scenarios match --only={args.only!r}.")
            return

    total = len(entries)
    passed = 0

    for idx, rec in enumerate(entries, start=1):
        name = rec["name"]
        desc = rec["description"]
        stdin_str = rec["stdin"]
        expected = rec["expected_stdout"]

        print(f"\n[{idx}/{total}] {name}")
        print(f"  {desc}")

        reset_db()
        actual = run_script(script, stdin_str)

        if actual == expected:
            print("  PASS")
            passed += 1
        else:
            print("  FAIL — diff:")
            for ln in difflib.unified_diff(
                expected.splitlines(),
                actual.splitlines(),
                fromfile="expected",
                tofile="yours",
                lineterm="",
            ):
                print(f"    {ln}")

    print(f"\n{'='*40}")
    print(f"Result: {passed}/{total} scenarios passed.")
    if passed < total:
        sys.exit(1)


if __name__ == "__main__":
    main()
