"""
Unit tests for PromptQueryService.

Tests run history, impact analysis queries, dependency stats,
and artifact digest lookups.
"""

import pytest
import tempfile
from pathlib import Path

from markitect.prompts.dependencies.models import DependencyEdge, EdgeType
from markitect.prompts.dependencies.repository import SQLiteDependencyRepository
from markitect.prompts.execution.models import PromptRun, RunStatus
from markitect.prompts.models import Artifact, ArtifactType
from markitect.prompts.repositories.sqlite import SQLiteArtifactRepository
from markitect.prompts.queries.operations import PromptQueryService


@pytest.fixture
def temp_db():
    """Create temporary database for testing."""
    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_repo(temp_db):
    return SQLiteArtifactRepository(temp_db)


@pytest.fixture
def dep_repo(temp_db):
    return SQLiteDependencyRepository(temp_db)


@pytest.fixture
def query_service(artifact_repo, dep_repo, temp_db):
    return PromptQueryService(artifact_repo, dep_repo, db_path=temp_db)


def _make_artifact(repo, space_id, name, content="content"):
    artifact = Artifact.create(
        space_id=space_id, name=name, content=content,
        artifact_type=ArtifactType.CONTENT,
    )
    return repo.create(artifact)


def _make_run(template_id, status=RunStatus.SUCCESS):
    run = PromptRun.create(
        template_id=template_id,
        input_bundle_hash="hash-abc",
    )
    if status == RunStatus.SUCCESS:
        run.mark_complete()
    elif status == RunStatus.FAILED:
        run.mark_failed("error")
    return run


def _make_edge(repo, source, target, run_id):
    edge = DependencyEdge.create(
        source_artifact_id=source,
        target_artifact_id=target,
        run_id=run_id,
        edge_type=EdgeType.REQUIRES,
    )
    return repo.create(edge)


class TestRunHistory:
    """Tests for get_run_history."""

    def test_returns_all_runs(self, query_service):
        """Test returning all registered runs."""
        run1 = _make_run("tmpl-1")
        run2 = _make_run("tmpl-2")
        query_service.register_run(run1)
        query_service.register_run(run2)

        history = query_service.get_run_history()
        assert len(history) == 2

    def test_filter_by_template(self, query_service):
        """Test filtering by template_id."""
        run1 = _make_run("tmpl-1")
        run2 = _make_run("tmpl-2")
        query_service.register_run(run1)
        query_service.register_run(run2)

        history = query_service.get_run_history(template_id="tmpl-1")
        assert len(history) == 1
        assert history[0]["template_id"] == "tmpl-1"

    def test_filter_by_status(self, query_service):
        """Test filtering by status."""
        run_ok = _make_run("tmpl-1", RunStatus.SUCCESS)
        run_fail = _make_run("tmpl-1", RunStatus.FAILED)
        query_service.register_run(run_ok)
        query_service.register_run(run_fail)

        history = query_service.get_run_history(status="success")
        assert len(history) == 1
        assert history[0]["status"] == "success"

    def test_limit(self, query_service):
        """Test limit parameter."""
        for i in range(10):
            run = _make_run(f"tmpl-{i}")
            query_service.register_run(run)

        history = query_service.get_run_history(limit=3)
        assert len(history) == 3

    def test_empty_history(self, query_service):
        """Test empty run history."""
        assert query_service.get_run_history() == []


class TestStaleArtifacts:
    """Tests for get_stale_artifacts."""

    def test_no_debt_returns_empty(self, query_service):
        """Test returns empty when no debt exists."""
        assert query_service.get_stale_artifacts() == []

    def test_no_engine_returns_empty(self, artifact_repo, dep_repo):
        """Test returns empty without db_path."""
        svc = PromptQueryService(artifact_repo, dep_repo, db_path=None)
        assert svc.get_stale_artifacts() == []


class TestDependencyStats:
    """Tests for get_dependency_stats."""

    def test_empty_graph(self, query_service):
        """Test stats for empty graph."""
        stats = query_service.get_dependency_stats()
        assert stats["total_nodes"] == 0
        assert stats["total_edges"] == 0
        assert stats["has_cycles"] is False

    def test_simple_graph(self, query_service, artifact_repo, dep_repo):
        """Test stats for a simple graph."""
        a = _make_artifact(artifact_repo, "s", "a")
        b = _make_artifact(artifact_repo, "s", "b")
        c = _make_artifact(artifact_repo, "s", "c")

        _make_edge(dep_repo, a.id, b.id, "r1")
        _make_edge(dep_repo, b.id, c.id, "r1")

        stats = query_service.get_dependency_stats()
        assert stats["total_nodes"] == 3
        assert stats["total_edges"] == 2
        assert stats["root_count"] == 1  # 'a' is root
        assert stats["leaf_count"] == 1  # 'c' is leaf
        assert stats["has_cycles"] is False


class TestFindArtifactsByDigest:
    """Tests for find_artifacts_by_digest."""

    def test_finds_matching(self, query_service, artifact_repo):
        """Test finding artifacts by content digest."""
        art = _make_artifact(artifact_repo, "s", "test-art", "hello")
        results = query_service.find_artifacts_by_digest(art.content_digest)
        assert len(results) == 1
        assert results[0]["artifact_id"] == art.id

    def test_no_match(self, query_service):
        """Test returns empty for non-matching digest."""
        assert query_service.find_artifacts_by_digest("nonexistent") == []

    def test_multiple_matches(self, query_service, artifact_repo):
        """Test finding multiple artifacts with same digest."""
        _make_artifact(artifact_repo, "s1", "a", "same-content")
        _make_artifact(artifact_repo, "s2", "a", "same-content")
        art = _make_artifact(artifact_repo, "s3", "a", "same-content")

        results = query_service.find_artifacts_by_digest(art.content_digest)
        assert len(results) == 3
