"""Claude-generated project context for enhanced code reviews.

This module manages AI-generated context about the project structure,
module relationships, and conventions. The context is built by Claude
analyzing the codebase and stored in a git-tracked JSON file.
"""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path


@dataclass
class ModuleContext:
    """AI-generated context for a single module."""

    path: str
    purpose: str = ""
    test_files: list[str] = field(default_factory=list)
    imports_from: list[str] = field(default_factory=list)
    imported_by: list[str] = field(default_factory=list)
    related_files: list[str] = field(default_factory=list)
    notes: str = ""

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return {
            "purpose": self.purpose,
            "test_files": self.test_files,
            "imports_from": self.imports_from,
            "imported_by": self.imported_by,
            "related_files": self.related_files,
            "notes": self.notes,
        }

    @classmethod
    def from_dict(cls, path: str, data: dict) -> ModuleContext:
        """Create from dictionary."""
        return cls(
            path=path,
            purpose=data.get("purpose", ""),
            test_files=data.get("test_files", []),
            imports_from=data.get("imports_from", []),
            imported_by=data.get("imported_by", []),
            related_files=data.get("related_files", []),
            notes=data.get("notes", ""),
        )


@dataclass
class ProjectContext:
    """AI-generated context for the entire project."""

    version: str = "1"
    generated_at: str = ""
    generated_commit: str = ""

    # Project-level context
    description: str = ""
    conventions: str = ""
    architecture: str = ""
    test_patterns: str = ""

    # Per-module context
    modules: dict[str, ModuleContext] = field(default_factory=dict)

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return {
            "version": self.version,
            "generated_at": self.generated_at,
            "generated_commit": self.generated_commit,
            "project": {
                "description": self.description,
                "conventions": self.conventions,
                "architecture": self.architecture,
                "test_patterns": self.test_patterns,
            },
            "modules": {path: ctx.to_dict() for path, ctx in self.modules.items()},
        }

    @classmethod
    def from_dict(cls, data: dict) -> ProjectContext:
        """Create from dictionary."""
        project_data = data.get("project", {})
        modules_data = data.get("modules", {})

        modules = {
            path: ModuleContext.from_dict(path, mod_data)
            for path, mod_data in modules_data.items()
        }

        return cls(
            version=data.get("version", "1"),
            generated_at=data.get("generated_at", ""),
            generated_commit=data.get("generated_commit", ""),
            description=project_data.get("description", ""),
            conventions=project_data.get("conventions", ""),
            architecture=project_data.get("architecture", ""),
            test_patterns=project_data.get("test_patterns", ""),
            modules=modules,
        )

    def get_module(self, path: str) -> ModuleContext | None:
        """Get context for a specific module."""
        return self.modules.get(path)

    def set_module(self, ctx: ModuleContext) -> None:
        """Set context for a module."""
        self.modules[ctx.path] = ctx


def load_context(path: Path) -> ProjectContext:
    """Load project context from JSON file.

    Args:
        path: Path to context JSON file.

    Returns:
        ProjectContext object (empty if file doesn't exist).
    """
    if not path.exists():
        return ProjectContext()

    with open(path) as f:
        data = json.load(f)

    return ProjectContext.from_dict(data)


def save_context(
    context: ProjectContext, path: Path, commit: str | None = None
) -> None:
    """Save project context to JSON file.

    Args:
        context: ProjectContext to save.
        path: Path to write to.
        commit: Current git commit SHA.
    """
    context.generated_at = datetime.now(timezone.utc).isoformat()
    if commit:
        context.generated_commit = commit

    path.parent.mkdir(parents=True, exist_ok=True)

    with open(path, "w") as f:
        json.dump(context.to_dict(), f, indent=2)
        f.write("\n")


def get_default_context_path(root: Path) -> Path:
    """Get the default path for the context file.

    Args:
        root: Project root directory.

    Returns:
        Path to .glreview/context.json
    """
    return root / ".glreview" / "context.json"


def format_module_context_for_prompt(
    module_ctx: ModuleContext | None,
    project_ctx: ProjectContext | None,
) -> str:
    """Format context for inclusion in a Claude review prompt.

    Args:
        module_ctx: Context for the specific module being reviewed.
        project_ctx: Overall project context.

    Returns:
        Formatted markdown string to include in prompt.
    """
    sections = []

    if project_ctx:
        if project_ctx.conventions:
            sections.append(f"## Project Conventions\n\n{project_ctx.conventions}")

        if project_ctx.architecture:
            sections.append(f"## Architecture Notes\n\n{project_ctx.architecture}")

    if module_ctx:
        if module_ctx.purpose:
            sections.append(f"## Module Purpose\n\n{module_ctx.purpose}")

        if module_ctx.test_files:
            test_list = "\n".join(f"- `{t}`" for t in module_ctx.test_files)
            sections.append(f"## Associated Test Files\n\n{test_list}")

        if module_ctx.imports_from:
            imports = "\n".join(f"- `{i}`" for i in module_ctx.imports_from)
            sections.append(f"## Dependencies (imports from)\n\n{imports}")

        if module_ctx.imported_by:
            importers = "\n".join(f"- `{i}`" for i in module_ctx.imported_by)
            sections.append(f"## Dependents (imported by)\n\n{importers}")

        if module_ctx.notes:
            sections.append(f"## Review Notes\n\n{module_ctx.notes}")

    if not sections:
        return ""

    return "\n\n".join(sections)
