"""Golden E2E tests for all project templates.

Validates the full create → generate → lint pipeline for every template.
"""

from __future__ import annotations

from pathlib import Path

import pytest

from tests.e2e.conftest import get_prism_command, run_command

# (template_name, expected_model_names, has_frontend)
TEMPLATE_CONFIGS: list[tuple[str, set[str], bool]] = [
    ("minimal", {"Item", "Category"}, False),
    ("api-only", {"Article", "Author"}, False),
    ("mcp-only", {"Dataset", "Record"}, False),
    ("website", {"Page", "Post", "Category"}, True),
    ("app", {"Project", "Task"}, True),
    ("enterprise-platform", {"Organization", "Project", "Document"}, True),
    ("saas", {"Tenant", "User", "Plan", "Subscription"}, True),
]

# Module-level cache: create + generate runs once per template
_cache: dict[str, Path] = {}


def _get_project(template: str, base_dir: Path) -> Path:
    """Get or create a project for the given template.

    Results are cached so create + generate only runs once per template.
    """
    if template not in _cache:
        run_command(
            [
                *get_prism_command(),
                "create",
                f"golden-{template}",
                "--template",
                template,
                "--no-install",
                "--no-git",
                "-y",
            ],
            cwd=base_dir,
        )
        project_dir = base_dir / f"golden-{template}"
        run_command(
            [*get_prism_command(), "generate"],
            cwd=project_dir,
        )
        _cache[template] = project_dir
    return _cache[template]


@pytest.fixture(scope="module")
def golden_base_dir(tmp_path_factory: pytest.TempPathFactory) -> Path:
    """Shared temporary directory for all golden tests."""
    return tmp_path_factory.mktemp("golden_templates")


@pytest.mark.e2e
class TestTemplateGolden:
    """Golden tests for all 7 project templates."""

    @pytest.mark.parametrize(
        "template,expected_models,has_frontend",
        TEMPLATE_CONFIGS,
        ids=[t[0] for t in TEMPLATE_CONFIGS],
    )
    def test_project_created(
        self,
        golden_base_dir: Path,
        template: str,
        expected_models: set[str],
        has_frontend: bool,
    ) -> None:
        """Project directory exists after create + generate."""
        project_dir = _get_project(template, golden_base_dir)
        assert project_dir.exists()
        assert project_dir.is_dir()

    @pytest.mark.parametrize(
        "template,expected_models,has_frontend",
        TEMPLATE_CONFIGS,
        ids=[t[0] for t in TEMPLATE_CONFIGS],
    )
    def test_spec_loadable(
        self,
        golden_base_dir: Path,
        template: str,
        expected_models: set[str],
        has_frontend: bool,
    ) -> None:
        """Spec can be loaded and model names match expected set."""
        from prisme.utils.spec_loader import load_spec_from_file

        project_dir = _get_project(template, golden_base_dir)
        spec_file = project_dir / "specs" / "models.py"
        assert spec_file.exists()

        loaded_spec = load_spec_from_file(spec_file)
        model_names = {m.name for m in loaded_spec.models}
        assert model_names == expected_models

    @pytest.mark.parametrize(
        "template,expected_models,has_frontend",
        TEMPLATE_CONFIGS,
        ids=[t[0] for t in TEMPLATE_CONFIGS],
    )
    def test_generate_succeeds(
        self,
        golden_base_dir: Path,
        template: str,
        expected_models: set[str],
        has_frontend: bool,
    ) -> None:
        """prisme generate exits 0 (already run by _get_project, verify output exists)."""
        project_dir = _get_project(template, golden_base_dir)
        # Generated code should exist — check for at least one generated Python file
        py_files = list(project_dir.rglob("_generated/*.py"))
        assert len(py_files) > 0, f"No generated Python files found in {project_dir}"

    @pytest.mark.parametrize(
        "template,expected_models,has_frontend",
        TEMPLATE_CONFIGS,
        ids=[t[0] for t in TEMPLATE_CONFIGS],
    )
    def test_ruff_check_passes(
        self,
        golden_base_dir: Path,
        template: str,
        expected_models: set[str],
        has_frontend: bool,
    ) -> None:
        """ruff check passes on all generated Python files."""
        import sys

        project_dir = _get_project(template, golden_base_dir)
        result = run_command(
            [sys.executable, "-m", "ruff", "check", "."],
            cwd=project_dir,
            check=False,
        )
        assert result.returncode == 0, f"ruff check failed:\n{result.stdout}\n{result.stderr}"

    @pytest.mark.parametrize(
        "template,expected_models,has_frontend",
        TEMPLATE_CONFIGS,
        ids=[t[0] for t in TEMPLATE_CONFIGS],
    )
    def test_ruff_format_clean(
        self,
        golden_base_dir: Path,
        template: str,
        expected_models: set[str],
        has_frontend: bool,
    ) -> None:
        """ruff format --check passes on all generated Python files."""
        import sys

        project_dir = _get_project(template, golden_base_dir)
        result = run_command(
            [sys.executable, "-m", "ruff", "format", "--check", "."],
            cwd=project_dir,
            check=False,
        )
        assert result.returncode == 0, f"ruff format failed:\n{result.stdout}\n{result.stderr}"
