"""Tests that generated TypeScript code passes eslint.

Generates all frontend code, sets up a minimal eslint environment,
then verifies eslint reports zero errors.

Marked @pytest.mark.slow because it runs npm install + eslint.
"""

from __future__ import annotations

import json
import subprocess
from typing import TYPE_CHECKING

import pytest

from prisme.generators.base import GeneratorContext
from prisme.generators.frontend import (
    ComponentsGenerator,
    DesignSystemGenerator,
    FrontendAuthGenerator,
    GraphQLOpsGenerator,
    HeadlessGenerator,
    HooksGenerator,
    PagesGenerator,
    RouterGenerator,
    TypeScriptGenerator,
    WidgetSystemGenerator,
)
from prisme.spec.fields import FieldSpec, FieldType
from prisme.spec.model import ModelSpec, RelationshipSpec
from prisme.spec.project import GeneratorConfig, ProjectSpec
from prisme.spec.stack import StackSpec

if TYPE_CHECKING:
    from pathlib import Path


def _make_package_json() -> dict:
    """Package.json with eslint dependencies."""
    return {
        "name": "prism-eslint-test",
        "private": True,
        "type": "module",
        "dependencies": {
            "react": "^18.2.0",
            "react-dom": "^18.2.0",
            "react-router-dom": "^6.20.0",
            "@apollo/client": "^3.8.0",
            "graphql": "^16.8.0",
        },
        "devDependencies": {
            "typescript": "^5.3.0",
            "@types/react": "^18.2.0",
            "@types/react-dom": "^18.2.0",
            "@eslint/js": "^9.18.0",
            "eslint": "^9.18.0",
            "eslint-plugin-react-hooks": "^5.0.0",
            "eslint-plugin-react-refresh": "^0.4.18",
            "globals": "^15.14.0",
            "typescript-eslint": "^8.20.0",
        },
    }


def _make_tsconfig() -> dict:
    """Minimal tsconfig.json."""
    return {
        "compilerOptions": {
            "target": "ES2020",
            "module": "ESNext",
            "moduleResolution": "bundler",
            "jsx": "react-jsx",
            "strict": True,
            "esModuleInterop": True,
            "skipLibCheck": True,
            "noEmit": True,
            "baseUrl": ".",
            "paths": {"@/*": ["src/*"]},
        },
        "include": ["src"],
    }


ESLINT_CONFIG = """\
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
  { ignores: ['dist'] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
    },
  },
)
"""


@pytest.mark.slow
class TestGeneratedTypeScriptEslintClean:
    """Verify that all generated frontend TypeScript passes eslint."""

    @pytest.fixture(scope="class")
    def stack_spec(self) -> StackSpec:
        """Create a representative stack spec."""
        return StackSpec(
            name="test-eslint",
            version="1.0.0",
            models=[
                ModelSpec(
                    name="Customer",
                    description="Customer entity",
                    timestamps=True,
                    fields=[
                        FieldSpec(name="name", type=FieldType.STRING, required=True),
                        FieldSpec(name="email", type=FieldType.STRING, required=True),
                        FieldSpec(name="age", type=FieldType.INTEGER, required=False),
                        FieldSpec(name="is_active", type=FieldType.BOOLEAN, required=False),
                    ],
                    relationships=[
                        RelationshipSpec(
                            name="orders",
                            target_model="Order",
                            type="one_to_many",
                            back_populates="customer",
                        ),
                    ],
                ),
                ModelSpec(
                    name="Order",
                    description="Order entity",
                    timestamps=True,
                    fields=[
                        FieldSpec(name="order_number", type=FieldType.STRING, required=True),
                        FieldSpec(name="total", type=FieldType.DECIMAL, required=True),
                        FieldSpec(
                            name="customer_id",
                            type=FieldType.FOREIGN_KEY,
                            references="Customer",
                        ),
                    ],
                    relationships=[
                        RelationshipSpec(
                            name="customer",
                            target_model="Customer",
                            type="many_to_one",
                            back_populates="orders",
                        ),
                    ],
                ),
            ],
        )

    @pytest.fixture(scope="class")
    def frontend_project(
        self, stack_spec: StackSpec, tmp_path_factory: pytest.TempPathFactory
    ) -> Path:
        """Set up project, generate all frontend code, install deps."""
        root = tmp_path_factory.mktemp("eslint")
        (root / "src").mkdir()

        # Write config files
        (root / "package.json").write_text(json.dumps(_make_package_json(), indent=2))
        (root / "tsconfig.json").write_text(json.dumps(_make_tsconfig(), indent=2))
        (root / "eslint.config.js").write_text(ESLINT_CONFIG)

        # Install dependencies
        result = subprocess.run(
            ["npm", "install", "--ignore-scripts"],
            cwd=root,
            capture_output=True,
            timeout=120,
        )
        if result.returncode != 0:
            pytest.skip(f"npm install failed: {result.stderr.decode()}")

        # Create generator context
        project = ProjectSpec(
            name="test-eslint",
            generator=GeneratorConfig(frontend_output="src"),
        )
        context = GeneratorContext(
            domain_spec=stack_spec,
            output_dir=root,
            project_spec=project,
        )

        # Run all frontend generators
        generators = [
            TypeScriptGenerator,
            GraphQLOpsGenerator,
            HeadlessGenerator,
            DesignSystemGenerator,
            WidgetSystemGenerator,
            ComponentsGenerator,
            HooksGenerator,
            PagesGenerator,
            FrontendAuthGenerator,
            RouterGenerator,
        ]

        for gen_cls in generators:
            try:
                gen = gen_cls(context)
                gen.generate()
            except Exception:
                pass

        return root

    def test_eslint_passes(self, frontend_project: Path) -> None:
        """All generated TypeScript passes eslint."""
        result = subprocess.run(
            ["npx", "eslint", "src/"],
            cwd=frontend_project,
            capture_output=True,
            text=True,
            timeout=120,
        )
        assert result.returncode == 0, f"eslint failed:\n{result.stdout}\n{result.stderr}"
