"""Unit tests for PromptResolver."""

import pytest
import tempfile
from pathlib import Path

from markitect.prompts.templates.models import PromptTemplate, ContentMacro, MacroKind
from markitect.prompts.templates.analyzer import TemplateAnalyzer
from markitect.prompts.resolver.resolver import PromptResolver
from markitect.prompts.resolver.strategy import MultiSpaceResolutionStrategy, ResolutionConfig
from markitect.prompts.resolver.models import ResolutionStatus
from markitect.prompts.services.artifact_service import ArtifactService
from markitect.prompts.repositories.sqlite import SQLiteArtifactRepository
from markitect.prompts.models import Artifact, ArtifactType


@pytest.fixture
def temp_db():
    """Create temporary database."""
    with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
        db_path = f.name
    yield db_path
    Path(db_path).unlink(missing_ok=True)


@pytest.fixture
def artifact_service(temp_db):
    """Create artifact service with temp database."""
    repository = SQLiteArtifactRepository(temp_db)
    return ArtifactService(repository)


@pytest.fixture
def resolver(artifact_service):
    """Create resolver with multi-space strategy."""
    strategy = MultiSpaceResolutionStrategy()
    return PromptResolver(artifact_service, strategy)


@pytest.fixture
def analyzer():
    """Create template analyzer."""
    return TemplateAnalyzer()


class TestPromptResolver:
    """Tests for PromptResolver."""

    def test_resolve_template_not_analyzed_raises_error(self, resolver):
        """Test resolving unanalyzed template raises error."""
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content="{{require:dep}}",
        )
        config = ResolutionConfig(space_id="space-1")

        with pytest.raises(ValueError, match="must be analyzed"):
            resolver.resolve_template(template, config)

    def test_resolve_template_no_macros(self, resolver, analyzer):
        """Test resolving template with no macros."""
        content = "# Simple Template\nNo macros here."
        template = PromptTemplate.create(
            space_id="space-1",
            name="simple",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        assert result.success is True
        assert result.status == ResolutionStatus.SUCCESS
        assert len(result.context.resolved_macros) == 0
        assert len(result.context.unresolved_required) == 0

    def test_resolve_required_macro_found(self, resolver, analyzer, artifact_service):
        """Test resolving required macro when artifact exists."""
        # Create dependency artifact
        artifact_service.create_artifact(
            space_id="space-1",
            name="glossary",
            content="Glossary content here",
        )

        # Create template with required macro
        content = "# Template\n{{require:glossary}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test-template",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        assert result.success is True
        assert result.status == ResolutionStatus.SUCCESS
        assert len(result.context.resolved_macros) == 1
        assert result.context.resolved_macros[0].resolved is True
        assert result.context.resolved_macros[0].artifact.name == "glossary"

    def test_resolve_required_macro_not_found_fails(self, resolver, analyzer):
        """Test resolving required macro when artifact missing (FR-3.2)."""
        content = "# Template\n{{require:missing-artifact}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        assert result.success is False
        assert result.status == ResolutionStatus.FAILED
        assert len(result.context.unresolved_required) == 1
        assert result.context.unresolved_required[0].target == "missing-artifact"
        assert len(result.context.errors) > 0

    def test_resolve_optional_macro_not_found_succeeds(self, resolver, analyzer):
        """Test resolving optional macro when missing succeeds (FR-3.3)."""
        content = "# Template\n{{optional:missing-optional}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        assert result.success is True  # Still succeeds
        assert result.status == ResolutionStatus.PARTIAL  # But partial
        assert len(result.context.unresolved_optional) == 1
        assert result.context.unresolved_optional[0].target == "missing-optional"

    def test_resolve_generate_macro_deferred(self, resolver, analyzer):
        """Test generate macro is detected and deferred (FR-3.4)."""
        content = "# Template\n{{generate:examples}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        assert result.success is True
        assert result.needs_generation is True
        assert len(result.context.generator_macros) == 1
        assert result.context.generator_macros[0].target == "examples"

    def test_resolve_multi_space_search_order(self, resolver, analyzer, artifact_service):
        """Test multi-space resolution follows search order (FR-3.1)."""
        # Create same-named artifact in multiple spaces
        artifact_service.create_artifact(
            space_id="space-1",
            name="common",
            content="From space-1",
        )
        artifact_service.create_artifact(
            space_id="space-2",
            name="common",
            content="From space-2",
        )

        content = "{{require:common}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        # space-1 has higher priority in search order
        config = ResolutionConfig(
            space_id="space-1",
            included_spaces=["space-2"],
        )
        result = resolver.resolve_template(template, config)

        # Should resolve from space-1 (higher priority)
        assert result.context.resolved_macros[0].space_id == "space-1"

    def test_resolve_falls_back_to_included_space(self, resolver, analyzer, artifact_service):
        """Test resolution falls back to included spaces."""
        # Create artifact only in included space
        artifact_service.create_artifact(
            space_id="space-2",
            name="shared-artifact",
            content="Shared content",
        )

        content = "{{require:shared-artifact}}"
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(
            space_id="space-1",
            included_spaces=["space-2"],
        )
        result = resolver.resolve_template(template, config)

        assert result.success is True
        assert result.context.resolved_macros[0].space_id == "space-2"

    def test_resolution_summary(self, resolver, analyzer, artifact_service):
        """Test getting resolution summary."""
        artifact_service.create_artifact(
            space_id="space-1",
            name="found",
            content="Content",
        )

        content = """
        {{require:found}}
        {{require:missing}}
        {{optional:optional-missing}}
        {{generate:gen}}
        """
        template = PromptTemplate.create(
            space_id="space-1",
            name="test",
            content=content,
        )
        analyzer.analyze(template, content)

        config = ResolutionConfig(space_id="space-1")
        result = resolver.resolve_template(template, config)

        summary = resolver.get_resolution_summary(result)
        assert summary["resolved_count"] == 1
        assert summary["unresolved_required"] == ["missing"]
        assert summary["unresolved_optional"] == ["optional-missing"]
        assert summary["needs_generation"] is True
        assert summary["generator_count"] == 1
