"""Generate versioned JSONL fixture files for regression testing.

Checks out dogcat code at each git tag, runs demo issue generation using
that version's code, and saves the resulting issues.jsonl (and inbox.jsonl
for tags that include the inbox system) as frozen fixtures in tests/fixtures/.

Only tags listed in SCHEMA_TAGS are generated by default — these are
the tags where the schema or demo output actually changed.  Pass a
specific tag to force generation.

Usage:
    python tests/generate_fixture.py              # Schema-changing tags only
    python tests/generate_fixture.py v0.3.0       # Specific tag (always runs)
"""

from __future__ import annotations

import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parent.parent
FIXTURES_DIR = REPO_ROOT / "tests" / "fixtures"

# Tags where the schema (models.py, demo.py, storage.py, inbox.py
# serialization) actually changed.  Only these tags produce distinct
# JSONL structures, so only these are generated by default.  Pass a
# tag explicitly to force generation of any other tag.
SCHEMA_TAGS: list[str] = [
    "v0.1.0",
    "v0.2.0",
    "v0.3.0",
    "v0.4.0",
    "v0.5.0",
    "v0.6.3",
    "v0.8.0",
    "v0.9.0",
    "v0.10.1",
]


def _generate_for_tag(tag: str, clone_dir: Path) -> list[Path]:
    """Generate fixture files for a specific git tag.

    Clones the repo (if not already cloned), checks out the tag,
    installs the package, and runs demo generation for issues and
    (when available) inbox proposals.

    Returns the paths to the generated fixture files.
    """
    issues_fixture = FIXTURES_DIR / f"{tag}_issues.jsonl"
    inbox_fixture = FIXTURES_DIR / f"{tag}_inbox.jsonl"

    if issues_fixture.exists():
        print(f"  Skipping {tag} — fixtures already exist")
        return [issues_fixture]

    # Clone if needed
    if not (clone_dir / ".git").exists():
        print(f"  Cloning repo to {clone_dir}")
        subprocess.run(
            ["git", "clone", "--quiet", str(REPO_ROOT), str(clone_dir)],
            check=True,
        )

    # Checkout the tag
    subprocess.run(
        ["git", "checkout", "--quiet", tag],
        cwd=clone_dir,
        check=True,
    )

    # Create a venv and install the package at this tag
    venv_dir = clone_dir / ".venv"
    if venv_dir.exists():
        shutil.rmtree(venv_dir)

    subprocess.run(
        ["uv", "venv", str(venv_dir), "--quiet"],
        cwd=clone_dir,
        check=True,
    )

    subprocess.run(
        [
            "uv",
            "pip",
            "install",
            "--quiet",
            "-e",
            str(clone_dir),
            "--python",
            str(venv_dir / "bin" / "python"),
        ],
        cwd=clone_dir,
        check=True,
    )

    python = str(venv_dir / "bin" / "python")
    generated: list[Path] = []

    # Generate demo issues in a temp .dogcats dir using the tagged code
    with tempfile.TemporaryDirectory() as tmpdir:
        dogcats_dir = Path(tmpdir) / ".dogcats"
        dogcats_dir.mkdir()
        issues_path = dogcats_dir / "issues.jsonl"

        script = f"""\
import sys
sys.path.insert(0, "{clone_dir / "src"}")
from dogcat.storage import JSONLStorage
from dogcat.demo import generate_demo_issues

storage = JSONLStorage("{issues_path}", create_dir=True)
ids = generate_demo_issues(storage, "{dogcats_dir}")
print(f"Generated {{len(ids)}} issues")
"""
        result = subprocess.run(
            [python, "-c", script],
            capture_output=True,
            text=True,
            cwd=clone_dir,
        )

        if result.returncode != 0:
            print(f"  ERROR generating issue fixture for {tag}:")
            print(f"    stdout: {result.stdout.strip()}")
            print(f"    stderr: {result.stderr.strip()}")
            msg = f"Failed to generate issue fixture for {tag}"
            raise RuntimeError(msg)

        print(f"  {tag}: {result.stdout.strip()}")

        FIXTURES_DIR.mkdir(parents=True, exist_ok=True)
        shutil.copy2(issues_path, issues_fixture)
        generated.append(issues_fixture)

        # Try to generate inbox fixtures (only available from v0.10.0+)
        inbox_script = f"""\
import sys
sys.path.insert(0, "{clone_dir / "src"}")
from dogcat.demo import generate_demo_inbox

count = generate_demo_inbox("{dogcats_dir}")
print(f"Generated {{count}} proposals")
"""
        inbox_result = subprocess.run(
            [python, "-c", inbox_script],
            capture_output=True,
            text=True,
            cwd=clone_dir,
        )

        inbox_path = dogcats_dir / "inbox.jsonl"
        if inbox_result.returncode == 0 and inbox_path.exists():
            print(f"  {tag}: {inbox_result.stdout.strip()}")
            shutil.copy2(inbox_path, inbox_fixture)
            generated.append(inbox_fixture)

    return generated


def main() -> None:
    """Generate fixtures for specified tags or all tags."""
    tags = sys.argv[1:] if len(sys.argv) > 1 else list(SCHEMA_TAGS)

    if not tags:
        print("No tags found")
        sys.exit(1)

    print(f"Generating fixtures for {len(tags)} tag(s)")
    FIXTURES_DIR.mkdir(parents=True, exist_ok=True)

    clone_dir = Path("/tmp/dcat-fixture-gen")
    if clone_dir.exists():
        shutil.rmtree(clone_dir)

    try:
        for tag in tags:
            print(f"Processing {tag}...")
            _generate_for_tag(tag, clone_dir)
    finally:
        if clone_dir.exists():
            shutil.rmtree(clone_dir)

    print(f"\nDone. Fixtures in {FIXTURES_DIR}/")


if __name__ == "__main__":
    main()
