"""Tests for validation metrics computation."""

from __future__ import annotations

from orxaq.fabric.validation.metrics import ValidationMetrics, compute_metrics


class TestComputeMetrics:
    def test_perfect_match(self):
        true = [("A", "B"), ("B", "C"), ("A", "C")]
        m = compute_metrics(true, true)
        assert m.precision == 1.0
        assert m.recall == 1.0
        assert m.f1 == 1.0
        assert m.shd == 0
        assert m.fdr == 0.0
        assert m.orientation_accuracy == 1.0

    def test_no_predictions(self):
        true = [("A", "B"), ("B", "C")]
        m = compute_metrics(true, [])
        assert m.precision == 0.0
        assert m.recall == 0.0
        assert m.f1 == 0.0
        assert m.shd == 2  # 2 missing edges

    def test_false_positives_only(self):
        true = [("A", "B")]
        pred = [("A", "B"), ("C", "D")]
        m = compute_metrics(true, pred)
        assert m.true_positives == 1
        assert m.false_positives == 1
        assert m.fdr == 0.5

    def test_reversed_edge(self):
        true = [("A", "B"), ("B", "C")]
        pred = [("B", "A"), ("B", "C")]
        m = compute_metrics(true, pred)
        assert m.reversed_edges == 1
        # SHD counts reversals.
        assert m.shd >= 1

    def test_missing_edges(self):
        true = [("A", "B"), ("B", "C"), ("A", "C")]
        pred = [("A", "B")]
        m = compute_metrics(true, pred)
        assert m.false_negatives == 2
        assert m.recall < 1.0

    def test_orientation_accuracy_partial(self):
        true = [("A", "B"), ("B", "C")]
        pred = [("A", "B"), ("C", "B")]  # Second edge reversed.
        m = compute_metrics(true, pred)
        assert m.orientation_accuracy == 0.5

    def test_empty_both(self):
        m = compute_metrics([], [])
        assert m.shd == 0
        assert m.f1 == 0.0

    def test_total_counts(self):
        true = [("A", "B"), ("B", "C"), ("C", "D")]
        pred = [("A", "B"), ("X", "Y")]
        m = compute_metrics(true, pred)
        assert m.total_true_edges == 3
        assert m.total_predicted_edges == 2


class TestValidationMetricsDataclass:
    def test_frozen(self):
        m = ValidationMetrics(
            shd=5,
            precision=0.8,
            recall=0.7,
            f1=0.74,
            orientation_accuracy=0.9,
            fdr=0.2,
            true_positives=7,
            false_positives=2,
            false_negatives=3,
            reversed_edges=1,
            total_true_edges=10,
            total_predicted_edges=9,
        )
        assert m.shd == 5
        assert m.f1 == 0.74


class TestReversedEdgeOrientation:
    """Cover line 84 (b→a form in both true and predicted)."""

    def test_backward_edge_orientation(self):
        """Line 84: edge (b,a) exists in both true_set and pred_set."""
        # ("B", "A") means b→a direction; undirected is (A,B) with A<B
        # Line 81 checks (a,b) in both — fails since ("A","B") not in sets
        # Line 83-84 checks (b,a) in both — triggers for ("B","A")
        true_edges = [("B", "A")]
        predicted = [("B", "A")]
        m = compute_metrics(true_edges, predicted)
        assert m.orientation_accuracy == 1.0
