"""Nikame Project Manifest — The Single Source of Truth for Generation.

Tracks every file, requirement, router, and lifespan hook across all
modules and features. Provides dependency resolution to ensure files
are generated in the correct topological order.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

import networkx as nx

from nikame.utils.logger import get_logger

_log = get_logger("codegen.manifest")


@dataclass
class RouterSpec:
    """Specification for a FastAPI router registration."""
    module_path: str  # e.g., "app.api.api_v1.endpoints.items"
    prefix: str       # e.g., "/items"
    tags: list[str]   # e.g., ["items"]
    router_var: str = "router"


@dataclass
class LifespanHook:
    """Startup or shutdown hook for the FastAPI application."""
    code: str
    name: str | None = None
    order: int = 50  # 0-100, lower runs earlier


@dataclass
class HealthCheckSpec:
    """Python-based health check for the API /health endpoint."""
    name: str
    expression: str  # Python bool expression, e.g., "db.is_healthy()"


@dataclass
class ProjectFile:
    """Metadata for a file being generated."""
    path: str
    content: str
    is_template: bool = False
    dependencies: set[str] = field(default_factory=set)  # Paths this file depends on


class ProjectManifest:
    """Central registry of all components to be generated.

    Modules register their requirements here during the planning phase,
    and the manifest provides the final validated assembly for main.py,
    requirements.txt, and the generation pipeline.
    """

    def __init__(self, project_name: str) -> None:
        self.project_name = project_name
        self.files: dict[str, ProjectFile] = {}
        self.requirements: set[str] = set()
        self.test_requirements: set[str] = set()
        self.routers: list[RouterSpec] = []
        self.lifespan_startup: list[LifespanHook] = []
        self.lifespan_shutdown: list[LifespanHook] = []
        self.health_checks: list[HealthCheckSpec] = []
        self.env_vars: dict[str, str] = {}

    def add_file(self, path: str, content: str, dependencies: set[str] = None) -> None:
        """Register a file to be written to disk."""
        self.files[path] = ProjectFile(
            path=path,
            content=content,
            dependencies=dependencies or set()
        )

    def add_requirement(self, package: str, is_test: bool = False) -> None:
        """Register a pip requirement."""
        if is_test:
            self.test_requirements.add(package)
        else:
            self.requirements.add(package)

    def add_router(self, spec: RouterSpec) -> None:
        """Register a FastAPI router."""
        self.routers.append(spec)

    def add_lifespan(self, hook_or_code: LifespanHook | str, startup: bool = True, order: int = 50) -> None:
        """Register a lifespan hook."""
        if isinstance(hook_or_code, LifespanHook):
            hook = hook_or_code
        else:
            hook = LifespanHook(code=hook_or_code, order=order)
            
        if startup:
            self.lifespan_startup.append(hook)
        else:
            self.lifespan_shutdown.append(hook)

    def add_health_check(self, name: str, expression: str) -> None:
        """Register a health check."""
        self.health_checks.append(HealthCheckSpec(name=name, expression=expression))

    def validate(self) -> None:
        """Validate the project for circular dependencies or missing requirements."""
        # TODO: Implement deep validation
        self._check_circular_dependencies()

    def _check_circular_dependencies(self) -> None:
        """Build a graph of files and find cycles."""
        dag = nx.DiGraph()
        for path, file_spec in self.files.items():
            dag.add_node(path)
            for dep in file_spec.dependencies:
                dag.add_edge(dep, path)

        if not nx.is_directed_acyclic_graph(dag):
            cycles = list(nx.simple_cycles(dag))
            _log.error("Circular dependencies detected in project files: %s", cycles)
            raise ValueError(f"Circular dependencies detected: {cycles}")

    def get_generation_order(self) -> list[str]:
        """Return files in topological order."""
        dag = nx.DiGraph()
        for path, file_spec in self.files.items():
            dag.add_node(path)
            for dep in file_spec.dependencies:
                dag.add_edge(dep, path)
        
        return list(nx.topological_sort(dag))

    def get_all_requirements(self) -> list[str]:
        """Return all pip requirements sorted."""
        return sorted(self.requirements)

    def get_all_test_requirements(self) -> list[str]:
        """Return all test pip requirements sorted."""
        return sorted(self.test_requirements)
