"""
Context compiler for assembling resolved prompts.

Compiles resolved macros into final prompt context.
"""

from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional

from markitect.prompts.templates.models import PromptTemplate
from markitect.prompts.resolver.models import ResolutionResult
from markitect.prompts.models import calculate_content_digest


@dataclass
class CompiledPrompt:
    """
    Compiled prompt ready for execution.

    Contains the final assembled prompt with all macros resolved.

    Attributes:
        template_id: Source template ID
        template_name: Source template name
        content: Compiled prompt content with macros substituted
        content_digest: SHA-256 digest of compiled content
        resolution_result: Original resolution result
        dependency_digests: Map of artifact name -> content digest
        compiled_at: Compilation timestamp
        metadata: Additional metadata
    """
    template_id: str
    template_name: str
    content: str
    content_digest: str
    resolution_result: ResolutionResult
    dependency_digests: Dict[str, str] = field(default_factory=dict)
    compiled_at: datetime = field(default_factory=datetime.utcnow)
    metadata: Dict[str, str] = field(default_factory=dict)

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "template_id": self.template_id,
            "template_name": self.template_name,
            "content_digest": self.content_digest,
            "content_length": len(self.content),
            "dependency_count": len(self.dependency_digests),
            "compiled_at": self.compiled_at.isoformat(),
            "resolution_status": self.resolution_result.status.value,
        }


class ContextCompiler:
    """
    Compiler for assembling resolved context into final prompt.

    Takes resolution results and produces CompiledPrompt with all
    macros substituted.
    """

    def compile(
        self,
        template: PromptTemplate,
        template_content: str,
        resolution_result: ResolutionResult,
    ) -> CompiledPrompt:
        """
        Compile template with resolved macros into final prompt.

        Args:
            template: Source template
            template_content: Original template content
            resolution_result: Resolution result with resolved macros

        Returns:
            CompiledPrompt with macros substituted

        Raises:
            ValueError: If resolution failed
        """
        if not resolution_result.success:
            raise ValueError(
                f"Cannot compile template '{template.name}': "
                f"Resolution failed with unresolved required macros"
            )

        # Start with original template content
        compiled_content = template_content

        # Track dependency digests
        dependency_digests = {}

        # Substitute each resolved macro
        for resolved in resolution_result.context.resolved_macros:
            if resolved.resolved and resolved.artifact:
                # Replace macro with resolved content
                compiled_content = compiled_content.replace(
                    resolved.macro.raw_text,
                    resolved.content,
                )
                # Track dependency
                dependency_digests[resolved.artifact.name] = resolved.artifact.content_digest

        # Substitute unresolved optional macros with empty string
        for macro in resolution_result.context.unresolved_optional:
            compiled_content = compiled_content.replace(macro.raw_text, "")

        # Calculate digest of compiled content
        content_digest = calculate_content_digest(compiled_content)

        return CompiledPrompt(
            template_id=template.id,
            template_name=template.name,
            content=compiled_content,
            content_digest=content_digest,
            resolution_result=resolution_result,
            dependency_digests=dependency_digests,
        )

    def compile_partial(
        self,
        template: PromptTemplate,
        template_content: str,
        resolution_result: ResolutionResult,
        placeholder: str = "[UNRESOLVED]",
    ) -> CompiledPrompt:
        """
        Compile template even with unresolved required macros.

        Useful for debugging or preview. Unresolved required macros
        are replaced with placeholder text.

        Args:
            template: Source template
            template_content: Original template content
            resolution_result: Resolution result (may have failures)
            placeholder: Text to use for unresolved macros

        Returns:
            CompiledPrompt with partial resolution
        """
        # Start with original template content
        compiled_content = template_content

        # Track dependency digests
        dependency_digests = {}

        # Substitute resolved macros
        for resolved in resolution_result.context.resolved_macros:
            if resolved.resolved and resolved.artifact:
                compiled_content = compiled_content.replace(
                    resolved.macro.raw_text,
                    resolved.content,
                )
                dependency_digests[resolved.artifact.name] = resolved.artifact.content_digest

        # Substitute unresolved required with placeholder
        for macro in resolution_result.context.unresolved_required:
            placeholder_text = f"{placeholder}:{macro.target}"
            compiled_content = compiled_content.replace(
                macro.raw_text,
                placeholder_text,
            )

        # Substitute unresolved optional with empty
        for macro in resolution_result.context.unresolved_optional:
            compiled_content = compiled_content.replace(macro.raw_text, "")

        content_digest = calculate_content_digest(compiled_content)

        return CompiledPrompt(
            template_id=template.id,
            template_name=template.name,
            content=compiled_content,
            content_digest=content_digest,
            resolution_result=resolution_result,
            dependency_digests=dependency_digests,
            metadata={"partial": "true", "placeholder": placeholder},
        )

    def get_compilation_info(self, compiled: CompiledPrompt) -> dict:
        """
        Get information about compilation.

        Args:
            compiled: Compiled prompt

        Returns:
            Dictionary with compilation metadata
        """
        return {
            "template_id": compiled.template_id,
            "template_name": compiled.template_name,
            "content_length": len(compiled.content),
            "content_digest": compiled.content_digest,
            "dependencies": list(compiled.dependency_digests.keys()),
            "dependency_count": len(compiled.dependency_digests),
            "compiled_at": compiled.compiled_at.isoformat(),
            "is_partial": compiled.metadata.get("partial") == "true",
        }
