"""
Tests for explicit symbolic path scoring in GraphReasoner.
"""

import networkx as nx
import pytest
from hieromem_core.symbolic import GraphReasoner


@pytest.fixture
def reasoner():
    return GraphReasoner()


@pytest.fixture
def graph():
    G = nx.DiGraph()
    # Chain 1: High trust, short
    G.add_edge("A", "B", relation="leads_to", trust=1.0, weight=1.0)
    G.add_edge("B", "C", relation="leads_to", trust=1.0, weight=1.0)

    # Chain 2: High trust, long
    G.add_edge("A", "D", relation="leads_to", trust=1.0, weight=1.0)
    G.add_edge("D", "E", relation="leads_to", trust=1.0, weight=1.0)
    G.add_edge("E", "C", relation="leads_to", trust=1.0, weight=1.0)

    # Chain 3: Low trust, short
    G.add_edge("A", "F", relation="leads_to", trust=0.1, weight=1.0)
    G.add_edge("F", "C", relation="leads_to", trust=1.0, weight=1.0)

    # Chain 4: Semantic mismatch (low weight), short
    G.add_edge("A", "G", relation="unrelated", trust=1.0, weight=0.1)
    G.add_edge("G", "C", relation="unrelated", trust=1.0, weight=0.1)

    return G


def test_scoring_monotonicity(reasoner, graph):
    """
    Verify monotonicity:
    1. Short perfect path > Long perfect path (Length penalty)
    2. High trust path > Low trust path
    3. High semantic path > Low semantic path
    """
    path_short_perfect = ["A", "B", "C"]
    path_long_perfect = ["A", "D", "E", "C"]
    path_low_trust = ["A", "F", "C"]
    path_low_semantic = ["A", "G", "C"]

    score_short = reasoner.score_path(graph, path_short_perfect)
    score_long = reasoner.score_path(graph, path_long_perfect)
    score_untrusted = reasoner.score_path(graph, path_low_trust)
    score_irrelevant = reasoner.score_path(graph, path_low_semantic)

    print(f"Short: {score_short.score:.4f}")
    print(f"Long: {score_long.score:.4f}")
    print(f"Untrusted: {score_untrusted.score:.4f}")
    print(f"Irrelevant: {score_irrelevant.score:.4f}")

    # 1. Length Penalty
    assert score_short.score > score_long.score, "Shorter paths should be preferred"  # nosec B101

    # 2. Trust Penalty
    assert score_short.score > score_untrusted.score, "Trusted paths should be preferred"  # nosec B101

    # 3. Semantic Relevance
    assert score_short.score > score_irrelevant.score, "Semantically relevant paths should be preferred"  # nosec B101


def test_score_components(reasoner):
    """Verify explicit decomposition of scores."""
    G = nx.DiGraph()
    G.add_edge("X", "Y", trust=0.5, weight=0.8)

    path = ["X", "Y"]
    scored = reasoner.score_path(G, path, w_trust=1.0, w_semantic=0.0, decay=1.0)
    # Trust only: 0.5 * 1.0 = 0.5
    assert scored.score == pytest.approx(0.5)  # nosec B101
    assert scored.trust_score == 0.5  # nosec B101

    scored = reasoner.score_path(G, path, w_trust=0.0, w_semantic=1.0, decay=1.0)
    # Semantic only: 0.8 * 1.0 = 0.8
    assert scored.score == pytest.approx(0.8)  # nosec B101
    assert scored.semantic_score == 0.8  # nosec B101

    scored = reasoner.score_path(G, path, w_trust=0.0, w_semantic=1.0, decay=0.5)
    # Decay: 0.5^1 = 0.5
    # Score: 0.8 * 0.5 = 0.4
    assert scored.score == pytest.approx(0.4)  # nosec B101
    assert scored.length_penalty == 0.5  # nosec B101


def test_invalid_paths(reasoner, graph):
    """Test handling of invalid/broken paths."""
    # Path with missing edge
    path_broken = ["A", "C"]  # No direct edge
    scored = reasoner.score_path(graph, path_broken)
    assert scored.score == 0.0  # nosec B101
    assert "Broken link" in scored.explanation  # nosec B101

    # Too short path
    path_single = ["A"]
    scored = reasoner.score_path(graph, path_single)
    assert scored.score == 0.0  # nosec B101
