"""React hooks generator for Prism.

Generates custom React hooks for data fetching and mutations
for each model.
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

from prism.generators.base import GeneratedFile, ModelGenerator
from prism.spec.stack import FileStrategy
from prism.utils.case_conversion import pluralize, to_camel_case, to_snake_case
from prism.utils.template_engine import TemplateRenderer

if TYPE_CHECKING:
    from prism.spec.model import ModelSpec


class HooksGenerator(ModelGenerator):
    """Generator for React hooks."""

    REQUIRED_TEMPLATES = [
        "frontend/hooks/urql_hooks.ts.jinja2",
        "frontend/hooks/apollo_hooks.ts.jinja2",
        "frontend/hooks/rest_hooks.ts.jinja2",
        "frontend/hooks/index.ts.jinja2",
    ]

    def __init__(self, *args, **kwargs) -> None:  # type: ignore[no-untyped-def]
        super().__init__(*args, **kwargs)
        frontend_base = Path(self.spec.generator.frontend_output)
        self.hooks_path = frontend_base / self.spec.generator.hooks_path
        self.renderer = TemplateRenderer()
        self.renderer.validate_templates_exist(self.REQUIRED_TEMPLATES)

    def generate_shared_files(self) -> list[GeneratedFile]:
        """No shared files for hooks."""
        return []

    def generate_model_files(self, model: ModelSpec) -> list[GeneratedFile]:
        """Generate hooks for a single model."""
        if not model.frontend.enabled:
            return []

        graphql_client = model.frontend.graphql_client

        if model.frontend.api_style == "graphql":
            if graphql_client == "urql":
                return [self._generate_urql_hooks(model)]
            else:
                return [self._generate_apollo_hooks(model)]
        else:
            return [self._generate_rest_hooks(model)]

    def generate_index_files(self) -> list[GeneratedFile]:
        """Generate index file for hooks."""
        return [self._generate_hooks_index()]

    def _generate_urql_hooks(self, model: ModelSpec) -> GeneratedFile:
        """Generate hooks using urql."""
        snake_name = to_snake_case(model.name)
        camel_name = to_camel_case(model.name)
        plural_name = pluralize(model.name)
        plural_camel = to_camel_case(pluralize(snake_name))
        ops = model.graphql.operations

        # Build loading and error expressions
        loading_parts = []
        if ops.create:
            loading_parts.append("createResult.fetching")
        if ops.update:
            loading_parts.append("updateResult.fetching")
        if ops.delete:
            loading_parts.append("deleteResult.fetching")

        error_parts = []
        if ops.create:
            error_parts.append("createResult.error")
        if ops.update:
            error_parts.append("updateResult.error")
        if ops.delete:
            error_parts.append("deleteResult.error")

        loading_expr = " || ".join(loading_parts) if loading_parts else "false"
        error_expr = (" ?? ".join(error_parts) if error_parts else "null") + " ?? null"

        content = self.renderer.render_file(
            "frontend/hooks/urql_hooks.ts.jinja2",
            context={
                "model_name": model.name,
                "camel_name": camel_name,
                "plural_name": plural_name,
                "plural_camel": plural_camel,
                "ops_create": ops.create,
                "ops_update": ops.update,
                "ops_delete": ops.delete,
                "loading_expr": loading_expr,
                "error_expr": error_expr,
            },
        )

        return GeneratedFile(
            path=self.hooks_path / f"use{model.name}.ts",
            content=content,
            strategy=FileStrategy.GENERATE_ONCE,
            description=f"Hooks for {model.name}",
        )

    def _generate_apollo_hooks(self, model: ModelSpec) -> GeneratedFile:
        """Generate hooks using Apollo Client."""
        snake_name = to_snake_case(model.name)
        camel_name = to_camel_case(model.name)
        plural_name = pluralize(model.name)
        plural_camel = to_camel_case(pluralize(snake_name))
        ops = model.graphql.operations

        # Build loading and error expressions
        loading_parts = []
        if ops.create:
            loading_parts.append("createState.loading")
        if ops.update:
            loading_parts.append("updateState.loading")
        if ops.delete:
            loading_parts.append("deleteState.loading")

        error_parts = []
        if ops.create:
            error_parts.append("createState.error")
        if ops.update:
            error_parts.append("updateState.error")
        if ops.delete:
            error_parts.append("deleteState.error")

        loading_expr = " || ".join(loading_parts) if loading_parts else "false"
        error_expr = (" ?? ".join(error_parts) if error_parts else "null") + " ?? null"

        content = self.renderer.render_file(
            "frontend/hooks/apollo_hooks.ts.jinja2",
            context={
                "model_name": model.name,
                "camel_name": camel_name,
                "plural_name": plural_name,
                "plural_camel": plural_camel,
                "ops_create": ops.create,
                "ops_update": ops.update,
                "ops_delete": ops.delete,
                "loading_expr": loading_expr,
                "error_expr": error_expr,
            },
        )

        return GeneratedFile(
            path=self.hooks_path / f"use{model.name}.ts",
            content=content,
            strategy=FileStrategy.GENERATE_ONCE,
            description=f"Hooks for {model.name}",
        )

    def _generate_rest_hooks(self, model: ModelSpec) -> GeneratedFile:
        """Generate hooks using REST API (fetch/SWR pattern)."""
        snake_name = to_snake_case(model.name)
        plural_name = pluralize(model.name)
        kebab_name = pluralize(snake_name).replace("_", "-")
        ops = model.frontend.operations

        content = self.renderer.render_file(
            "frontend/hooks/rest_hooks.ts.jinja2",
            context={
                "model_name": model.name,
                "plural_name": plural_name,
                "kebab_name": kebab_name,
                "ops_create": ops.create,
                "ops_update": ops.update,
                "ops_delete": ops.delete,
            },
        )

        return GeneratedFile(
            path=self.hooks_path / f"use{model.name}.ts",
            content=content,
            strategy=FileStrategy.GENERATE_ONCE,
            description=f"Hooks for {model.name}",
        )

    def _generate_hooks_index(self) -> GeneratedFile:
        """Generate index file for hooks."""
        model_names = [model.name for model in self.spec.models if model.frontend.enabled]

        content = self.renderer.render_file(
            "frontend/hooks/index.ts.jinja2",
            context={"model_names": model_names},
        )

        return GeneratedFile(
            path=self.hooks_path / "index.ts",
            content=content,
            strategy=FileStrategy.ALWAYS_OVERWRITE,
            description="Hooks index",
        )


__all__ = ["HooksGenerator"]
