"""Tests for data lineage tracking."""

from __future__ import annotations

from orxaq.fabric.lineage import LineageTracker
from orxaq.kernel import AuditLog, WorldDAG


class TestLineageTracker:
    def setup_method(self):
        self.dag = WorldDAG()
        self.audit = AuditLog()
        self.tracker = LineageTracker(self.dag, self.audit)

    def test_record_ingest_creates_entities(self):
        self.tracker.record_ingest("loans.csv", "loans_raw", ["id", "amount"])
        assert self.dag.get_entity("loans.csv") is not None
        assert self.dag.get_entity("loans_raw") is not None

    def test_record_ingest_creates_edge(self):
        self.tracker.record_ingest("loans.csv", "loans_raw")
        assert "loans_raw" in self.dag.children("loans.csv")

    def test_record_ingest_logs_to_audit(self):
        self.tracker.record_ingest("loans.csv", "loans_raw")
        entries = self.audit.search(action="data_ingest")
        assert len(entries) == 1
        assert "loans_raw" in entries[0].details

    def test_record_transform_creates_edge(self):
        self.tracker.record_ingest("src.csv", "raw")
        self.tracker.record_transform("raw", "filtered", "filter(dti > 0.43)")
        assert "filtered" in self.dag.children("raw")

    def test_record_transform_logs_to_audit(self):
        self.tracker.record_ingest("src.csv", "raw")
        self.tracker.record_transform("raw", "clean", "deduplicate")
        entries = self.audit.search(action="data_transform")
        assert len(entries) == 1

    def test_trace_upstream(self):
        self.tracker.record_ingest("source_a", "dataset_1")
        self.tracker.record_transform("dataset_1", "dataset_2", "transform_x")
        upstream = self.tracker.trace_upstream("dataset_2")
        assert "source_a" in upstream
        assert "dataset_1" in upstream

    def test_trace_downstream(self):
        self.tracker.record_ingest("source_a", "dataset_1")
        self.tracker.record_transform("dataset_1", "dataset_2", "transform_x")
        downstream = self.tracker.trace_downstream("source_a")
        assert "dataset_1" in downstream
        assert "dataset_2" in downstream

    def test_impact_analysis(self):
        self.tracker.record_ingest("raw_source", "step_1")
        self.tracker.record_transform("step_1", "step_2", "clean")
        self.tracker.record_transform("step_2", "step_3", "enrich")

        impact = self.tracker.impact_analysis("raw_source")
        assert "step_1" in impact
        assert "step_2" in impact
        assert "step_3" in impact
        assert impact["step_1"] < impact["step_3"]  # closer = smaller distance

    def test_impact_analysis_empty(self):
        self.tracker.record_ingest("isolated", "lonely_dataset")
        impact = self.tracker.impact_analysis("lonely_dataset")
        assert len(impact) == 0

    def test_multiple_sources(self):
        self.tracker.record_ingest("source_a", "ds_a")
        self.tracker.record_ingest("source_b", "ds_b")
        assert self.dag.node_count() == 4  # 2 sources + 2 datasets


# ── Targeted coverage (line 143: cycle/visited skip in impact_analysis) ──


class TestImpactAnalysisCycle:
    """Cover line 143: visited node skipped in BFS traversal."""

    def test_diamond_convergence(self):
        """Diamond: A→B, A→C, B→D, C→D. D should appear once at distance 2."""
        dag = WorldDAG()
        audit = AuditLog()
        tracker = LineageTracker(dag, audit)
        tracker.record_ingest("src", "A")
        tracker.record_transform("A", "B", "step1")
        tracker.record_transform("A", "C", "step2")
        tracker.record_transform("B", "D", "merge1")
        tracker.record_transform("C", "D", "merge2")
        impact = tracker.impact_analysis("A")
        # D reachable via both B and C — visited skip prevents double processing
        assert "D" in impact
        assert "B" in impact
        assert "C" in impact
