"""
Models for resolution engine.

Defines resolution context, results, and error types for macro resolution.
"""

from dataclasses import dataclass, field
from typing import List, Dict, Optional, Any
from enum import Enum

from markitect.prompts.templates.models import ContentMacro, MacroKind
from markitect.prompts.models import Artifact


class ResolutionStatus(Enum):
    """Status of resolution operation."""
    SUCCESS = "success"
    PARTIAL = "partial"  # Some optional macros missing
    FAILED = "failed"  # Required macros missing


class ResolutionError(Exception):
    """Raised when macro resolution fails."""

    def __init__(self, message: str, macro: Optional[ContentMacro] = None):
        super().__init__(message)
        self.macro = macro


@dataclass
class ResolvedMacro:
    """
    Result of resolving a single macro.

    Attributes:
        macro: Original macro
        artifact: Resolved artifact (None if not found)
        resolved: Whether artifact was found
        space_id: Space where artifact was found
        content: Resolved content (empty string if not found for optional)
    """
    macro: ContentMacro
    artifact: Optional[Artifact] = None
    resolved: bool = False
    space_id: Optional[str] = None
    content: str = ""

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "macro": self.macro.to_dict(),
            "artifact_id": self.artifact.id if self.artifact else None,
            "artifact_name": self.artifact.name if self.artifact else None,
            "resolved": self.resolved,
            "space_id": self.space_id,
            "content_preview": self.content[:100] + "..." if len(self.content) > 100 else self.content,
        }


@dataclass
class ResolutionContext:
    """
    Context for macro resolution.

    Tracks resolution state, search order, and resolved artifacts.

    Attributes:
        template_id: ID of template being resolved
        space_id: Primary space ID
        search_order: Ordered list of space IDs to search
        resolved_macros: List of resolved macros
        unresolved_required: List of unresolved required macros
        unresolved_optional: List of unresolved optional macros
        generator_macros: List of generate macros (deferred)
        errors: List of resolution errors
        metadata: Additional context metadata
    """
    template_id: str
    space_id: str
    search_order: List[str] = field(default_factory=list)
    resolved_macros: List[ResolvedMacro] = field(default_factory=list)
    unresolved_required: List[ContentMacro] = field(default_factory=list)
    unresolved_optional: List[ContentMacro] = field(default_factory=list)
    generator_macros: List[ContentMacro] = field(default_factory=list)
    errors: List[str] = field(default_factory=list)
    metadata: Dict[str, Any] = field(default_factory=dict)

    def add_resolved(self, resolved: ResolvedMacro) -> None:
        """Add a resolved macro to context."""
        self.resolved_macros.append(resolved)

    def add_unresolved_required(self, macro: ContentMacro) -> None:
        """Record an unresolved required macro."""
        self.unresolved_required.append(macro)
        self.errors.append(
            f"Required macro '{macro.target}' not found (line {macro.line_number})"
        )

    def add_unresolved_optional(self, macro: ContentMacro) -> None:
        """Record an unresolved optional macro."""
        self.unresolved_optional.append(macro)

    def add_generator(self, macro: ContentMacro) -> None:
        """Record a generator macro for deferred execution."""
        self.generator_macros.append(macro)

    def has_errors(self) -> bool:
        """Check if any resolution errors occurred."""
        return len(self.errors) > 0

    def get_status(self) -> ResolutionStatus:
        """Get overall resolution status."""
        if self.unresolved_required:
            return ResolutionStatus.FAILED
        elif self.unresolved_optional:
            return ResolutionStatus.PARTIAL
        else:
            return ResolutionStatus.SUCCESS

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "template_id": self.template_id,
            "space_id": self.space_id,
            "search_order": self.search_order,
            "resolved_count": len(self.resolved_macros),
            "unresolved_required": [m.target for m in self.unresolved_required],
            "unresolved_optional": [m.target for m in self.unresolved_optional],
            "generator_count": len(self.generator_macros),
            "status": self.get_status().value,
            "errors": self.errors,
        }


@dataclass
class ResolutionResult:
    """
    Result of template resolution.

    Contains resolved content and metadata about the resolution process.

    Attributes:
        context: Resolution context with full state
        success: Whether all required macros were resolved
        resolved_content: Dictionary of macro -> resolved content
        dependency_artifacts: List of artifact IDs used
        needs_generation: Whether any generate macros were found
    """
    context: ResolutionContext
    success: bool
    resolved_content: Dict[str, str] = field(default_factory=dict)
    dependency_artifacts: List[str] = field(default_factory=list)
    needs_generation: bool = False

    @property
    def status(self) -> ResolutionStatus:
        """Get resolution status."""
        return self.context.get_status()

    def get_resolved_macro(self, target: str) -> Optional[ResolvedMacro]:
        """
        Get resolved macro by target name.

        Args:
            target: Macro target name

        Returns:
            ResolvedMacro if found, None otherwise
        """
        for resolved in self.context.resolved_macros:
            if resolved.macro.target == target:
                return resolved
        return None

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "success": self.success,
            "status": self.status.value,
            "context": self.context.to_dict(),
            "dependency_count": len(self.dependency_artifacts),
            "needs_generation": self.needs_generation,
        }
