"""
Integration tests for dependency graph full workflow.

Tests the complete workflow: artifacts + edges + graph + queries
across multiple information spaces.
"""

import pytest
import tempfile
from pathlib import Path

from markitect.prompts.models import Artifact, ArtifactType
from markitect.prompts.repositories.sqlite import SQLiteArtifactRepository
from markitect.prompts.dependencies.models import (
    DependencyEdge,
    DependencyGraph,
    EdgeType,
)
from markitect.prompts.dependencies.repository import SQLiteDependencyRepository
from markitect.prompts.dependencies.graph import GraphBuilder
from markitect.prompts.dependencies.queries import DependencyQueryService
from markitect.prompts.execution.manifest import RunManifest


@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):
    """Create artifact repository."""
    return SQLiteArtifactRepository(temp_db)


@pytest.fixture
def dep_repo(temp_db):
    """Create dependency repository (shares same DB)."""
    return SQLiteDependencyRepository(temp_db)


@pytest.fixture
def builder(dep_repo):
    """Create GraphBuilder."""
    return GraphBuilder(dep_repo)


@pytest.fixture
def query_service(dep_repo):
    """Create DependencyQueryService."""
    return DependencyQueryService(dep_repo)


def _create_artifact(artifact_repo, space_id, name, content="test content"):
    """Helper to create and persist an artifact."""
    artifact = Artifact.create(
        space_id=space_id,
        name=name,
        content=content,
        artifact_type=ArtifactType.CONTENT,
    )
    return artifact_repo.create(artifact)


class TestFullWorkflow:
    """Tests for the complete dependency tracking workflow."""

    def test_manifest_to_graph_workflow(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test full workflow: create artifacts, manifest, persist edges, query."""
        # Step 1: Create artifacts in a space
        art_a = _create_artifact(artifact_repo, "space-1", "artifact-a", "content-a")
        art_b = _create_artifact(artifact_repo, "space-1", "artifact-b", "content-b")
        art_c = _create_artifact(artifact_repo, "space-1", "artifact-c", "content-c")

        # Step 2: Create a manifest with dependency edges
        manifest = RunManifest.create(
            run_id="run-1",
            template_id=art_a.id,
            template_name="artifact-a",
            template_digest=art_a.content_digest,
        )
        manifest.add_dependency_edge(art_a.id, art_b.id, "requires")
        manifest.add_dependency_edge(art_b.id, art_c.id, "requires")

        # Step 3: Persist edges from manifest
        persisted = builder.persist_edges(manifest)
        assert len(persisted) == 2

        # Step 4: Build graph
        graph = builder.build_graph()
        assert graph.has_edge(art_a.id, art_b.id)
        assert graph.has_edge(art_b.id, art_c.id)

        # Step 5: Query dependencies
        deps = query_service.find_dependencies(art_a.id)
        assert art_b.id in deps

        transitive_deps = query_service.find_transitive_dependencies(art_a.id)
        assert art_b.id in transitive_deps
        assert art_c.id in transitive_deps

        # Step 6: Query dependents
        dependents = query_service.find_dependents(art_c.id)
        assert art_b.id in dependents

        transitive_dependents = query_service.find_transitive_dependents(art_c.id)
        assert art_a.id in transitive_dependents
        assert art_b.id in transitive_dependents

    def test_build_order_workflow(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test build order across a dependency graph."""
        art_a = _create_artifact(artifact_repo, "space-1", "lib-core")
        art_b = _create_artifact(artifact_repo, "space-1", "lib-utils")
        art_c = _create_artifact(artifact_repo, "space-1", "app")

        # app depends on lib-utils, lib-utils depends on lib-core
        manifest = RunManifest.create(
            run_id="run-build",
            template_id=art_c.id,
            template_name="app",
            template_digest=art_c.content_digest,
        )
        manifest.add_dependency_edge(art_c.id, art_b.id, "requires")
        manifest.add_dependency_edge(art_b.id, art_a.id, "requires")

        builder.persist_edges(manifest)

        # Build order should have core first, then utils, then app
        order = query_service.get_build_order()
        assert order.index(art_a.id) < order.index(art_b.id)
        assert order.index(art_b.id) < order.index(art_c.id)

    def test_dependency_chain_workflow(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test finding dependency chains between artifacts."""
        art_a = _create_artifact(artifact_repo, "space-1", "source")
        art_b = _create_artifact(artifact_repo, "space-1", "intermediate")
        art_c = _create_artifact(artifact_repo, "space-1", "target")

        manifest = RunManifest.create(
            run_id="run-chain",
            template_id=art_a.id,
            template_name="source",
            template_digest=art_a.content_digest,
        )
        manifest.add_dependency_edge(art_a.id, art_b.id, "requires")
        manifest.add_dependency_edge(art_b.id, art_c.id, "requires")

        builder.persist_edges(manifest)

        chain = query_service.get_dependency_chain(art_a.id, art_c.id)
        assert chain is not None
        assert chain[0] == art_a.id
        assert chain[-1] == art_c.id
        assert len(chain) == 3


class TestCrossSpaceGraph:
    """Tests for dependency tracking across information spaces (FR-6.2)."""

    def test_cross_space_dependencies(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test dependencies between artifacts in different spaces."""
        # Create artifacts in different spaces
        shared = _create_artifact(artifact_repo, "shared", "common-lib")
        team_a = _create_artifact(artifact_repo, "team-a", "service-a")
        team_b = _create_artifact(artifact_repo, "team-b", "service-b")

        # Both services depend on shared lib
        manifest = RunManifest.create(
            run_id="run-cross-space",
            template_id=team_a.id,
            template_name="service-a",
            template_digest=team_a.content_digest,
        )
        manifest.add_dependency_edge(team_a.id, shared.id, "requires")
        manifest.add_dependency_edge(team_b.id, shared.id, "requires")

        builder.persist_edges(manifest)

        # Shared lib should have both services as dependents
        dependents = query_service.find_dependents(shared.id)
        assert team_a.id in dependents
        assert team_b.id in dependents

        # Each service should have shared as a dependency
        assert shared.id in query_service.find_dependencies(team_a.id)
        assert shared.id in query_service.find_dependencies(team_b.id)

    def test_cross_space_transitive(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test transitive dependencies across spaces."""
        # shared/foundation -> shared/utils -> team-a/app
        foundation = _create_artifact(artifact_repo, "shared", "foundation")
        utils = _create_artifact(artifact_repo, "shared", "utils")
        app = _create_artifact(artifact_repo, "team-a", "app")

        manifest = RunManifest.create(
            run_id="run-trans",
            template_id=app.id,
            template_name="app",
            template_digest=app.content_digest,
        )
        manifest.add_dependency_edge(app.id, utils.id, "requires")
        manifest.add_dependency_edge(utils.id, foundation.id, "requires")

        builder.persist_edges(manifest)

        # App should transitively depend on foundation
        transitive = query_service.find_transitive_dependencies(app.id)
        assert utils.id in transitive
        assert foundation.id in transitive

    def test_cross_space_build_order(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test build order across spaces."""
        core = _create_artifact(artifact_repo, "shared", "core")
        api = _create_artifact(artifact_repo, "team-a", "api")
        web = _create_artifact(artifact_repo, "team-b", "web")

        manifest = RunManifest.create(
            run_id="run-order",
            template_id=web.id,
            template_name="web",
            template_digest=web.content_digest,
        )
        manifest.add_dependency_edge(api.id, core.id, "requires")
        manifest.add_dependency_edge(web.id, api.id, "requires")

        builder.persist_edges(manifest)

        order = query_service.get_build_order()
        assert order.index(core.id) < order.index(api.id)
        assert order.index(api.id) < order.index(web.id)


class TestMultipleRuns:
    """Tests for dependency tracking across multiple runs."""

    def test_edges_from_multiple_runs(
        self, artifact_repo, dep_repo, builder, query_service
    ):
        """Test edges from multiple runs are combined in graph."""
        art_a = _create_artifact(artifact_repo, "space-1", "a")
        art_b = _create_artifact(artifact_repo, "space-1", "b")
        art_c = _create_artifact(artifact_repo, "space-1", "c")

        # Run 1: A -> B
        manifest1 = RunManifest.create(
            run_id="run-1",
            template_id=art_a.id,
            template_name="a",
            template_digest=art_a.content_digest,
        )
        manifest1.add_dependency_edge(art_a.id, art_b.id, "requires")
        builder.persist_edges(manifest1)

        # Run 2: B -> C
        manifest2 = RunManifest.create(
            run_id="run-2",
            template_id=art_b.id,
            template_name="b",
            template_digest=art_b.content_digest,
        )
        manifest2.add_dependency_edge(art_b.id, art_c.id, "requires")
        builder.persist_edges(manifest2)

        # Full graph should have A -> B -> C
        graph = builder.build_graph()
        assert graph.has_edge(art_a.id, art_b.id)
        assert graph.has_edge(art_b.id, art_c.id)

        # Transitive query should work across runs
        transitive = query_service.find_transitive_dependencies(art_a.id)
        assert art_b.id in transitive
        assert art_c.id in transitive

    def test_run_scoped_graph(self, artifact_repo, dep_repo, builder):
        """Test building graph scoped to a specific run."""
        art_a = _create_artifact(artifact_repo, "space-1", "a")
        art_b = _create_artifact(artifact_repo, "space-1", "b")
        art_c = _create_artifact(artifact_repo, "space-1", "c")

        # Persist edges for different runs
        edge1 = DependencyEdge.create(
            source_artifact_id=art_a.id,
            target_artifact_id=art_b.id,
            run_id="run-1",
            edge_type=EdgeType.REQUIRES,
        )
        dep_repo.create(edge1)

        edge2 = DependencyEdge.create(
            source_artifact_id=art_b.id,
            target_artifact_id=art_c.id,
            run_id="run-2",
            edge_type=EdgeType.GENERATES,
        )
        dep_repo.create(edge2)

        # Graph for run-1 only
        graph1 = builder.build_graph_for_run("run-1")
        assert graph1.has_edge(art_a.id, art_b.id)
        assert not graph1.has_edge(art_b.id, art_c.id)

        # Graph for run-2 only
        graph2 = builder.build_graph_for_run("run-2")
        assert not graph2.has_edge(art_a.id, art_b.id)
        assert graph2.has_edge(art_b.id, art_c.id)
