"""Tests for semantically_similar_to edge support."""
import networkx as nx
import pytest
from graphify.build import build_from_json
from graphify.analyze import surprising_connections, _surprise_score
from graphify.report import generate


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _make_extraction_with_semantic_edge():
    """Two nodes in separate files connected by a semantically_similar_to edge."""
    return {
        "nodes": [
            {"id": "a_validate_input", "label": "validate_input", "file_type": "code",
             "source_file": "auth/validators.py", "source_location": "L5"},
            {"id": "b_check_input", "label": "check_input", "file_type": "code",
             "source_file": "api/checks.py", "source_location": "L12"},
        ],
        "edges": [
            {
                "source": "a_validate_input",
                "target": "b_check_input",
                "relation": "semantically_similar_to",
                "confidence": "INFERRED",
                "confidence_score": 0.82,
                "source_file": "auth/validators.py",
                "source_location": None,
                "weight": 0.82,
            }
        ],
        "input_tokens": 100,
        "output_tokens": 50,
    }


def _make_graph_with_semantic_edge():
    return build_from_json(_make_extraction_with_semantic_edge())


def _make_two_edge_graph():
    """Graph with one semantically_similar_to edge and one references edge, both cross-file."""
    G = nx.Graph()
    for nid, label, src in [
        ("a", "ValidateInput", "auth/validators.py"),
        ("b", "CheckInput", "api/checks.py"),
        ("c", "LoadConfig", "config/loader.py"),
        ("d", "ReadConfig", "utils/reader.py"),
    ]:
        G.add_node(nid, label=label, source_file=src, file_type="code")
    # semantically_similar_to edge
    G.add_edge("a", "b", relation="semantically_similar_to", confidence="INFERRED",
               confidence_score=0.82, source_file="auth/validators.py", weight=0.82,
               _src="a", _tgt="b")
    # plain references edge (same confidence tier)
    G.add_edge("c", "d", relation="references", confidence="INFERRED",
               confidence_score=0.7, source_file="config/loader.py", weight=0.7,
               _src="c", _tgt="d")
    return G


# ---------------------------------------------------------------------------
# Test 1: semantically_similar_to passes through build_from_json without being dropped
# ---------------------------------------------------------------------------

def test_semantic_edge_survives_build_from_json():
    G = _make_graph_with_semantic_edge()
    assert G.number_of_edges() == 1
    u, v, data = next(iter(G.edges(data=True)))
    assert data["relation"] == "semantically_similar_to"


def test_semantic_edge_nodes_present():
    G = _make_graph_with_semantic_edge()
    assert "a_validate_input" in G.nodes
    assert "b_check_input" in G.nodes


# ---------------------------------------------------------------------------
# Test 2: confidence_score is preserved for semantically_similar_to edges
# ---------------------------------------------------------------------------

def test_semantic_edge_confidence_score_preserved():
    G = _make_graph_with_semantic_edge()
    u, v, data = next(iter(G.edges(data=True)))
    assert data.get("confidence_score") == pytest.approx(0.82)
    assert data.get("confidence") == "INFERRED"


# ---------------------------------------------------------------------------
# Test 3: surprising_connections scores semantically_similar_to edges higher
#         than references edges with the same community membership
# ---------------------------------------------------------------------------

def test_semantic_edge_scores_higher_than_references():
    G = _make_two_edge_graph()
    communities = {0: ["a", "b"], 1: ["c", "d"]}
    node_community = {"a": 0, "b": 0, "c": 1, "d": 1}

    score_sem, reasons_sem = _surprise_score(
        G, "a", "b", G.edges["a", "b"], node_community,
        "auth/validators.py", "api/checks.py"
    )
    score_ref, _ = _surprise_score(
        G, "c", "d", G.edges["c", "d"], node_community,
        "config/loader.py", "utils/reader.py"
    )
    assert score_sem > score_ref


def test_semantic_edge_reason_mentions_similarity():
    G = _make_two_edge_graph()
    communities = {0: ["a", "b"], 1: ["c", "d"]}
    node_community = {"a": 0, "b": 0, "c": 1, "d": 1}

    _, reasons = _surprise_score(
        G, "a", "b", G.edges["a", "b"], node_community,
        "auth/validators.py", "api/checks.py"
    )
    assert any("similar" in r for r in reasons)


# ---------------------------------------------------------------------------
# Test 4: report renders [semantically similar] tag for these edges
# ---------------------------------------------------------------------------

def _make_report_with_semantic_surprise():
    G = _make_graph_with_semantic_edge()
    communities = {0: ["a_validate_input", "b_check_input"]}
    cohesion = {0: 0.5}
    labels = {0: "Validators"}
    gods = []
    surprises = [
        {
            "source": "validate_input",
            "target": "check_input",
            "relation": "semantically_similar_to",
            "confidence": "INFERRED",
            "confidence_score": 0.82,
            "source_files": ["auth/validators.py", "api/checks.py"],
            "why": "semantically similar concepts with no structural link",
        }
    ]
    detection = {"total_files": 2, "total_words": 500, "needs_graph": True, "warning": None}
    tokens = {"input": 100, "output": 50}
    return generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project")


def test_report_renders_semantically_similar_tag():
    report = _make_report_with_semantic_surprise()
    assert "[semantically similar]" in report


def test_report_semantic_tag_on_correct_line():
    report = _make_report_with_semantic_surprise()
    for line in report.splitlines():
        if "semantically_similar_to" in line:
            assert "[semantically similar]" in line
            break
    else:
        pytest.fail("No line with semantically_similar_to found in report")


def test_report_no_semantic_tag_for_other_relations():
    """Non-semantic edges must not get the [semantically similar] tag."""
    G = nx.Graph()
    for nid, label, src in [
        ("x", "Alpha", "repo1/a.py"),
        ("y", "Beta", "repo2/b.py"),
    ]:
        G.add_node(nid, label=label, source_file=src, file_type="code")
    G.add_edge("x", "y", relation="references", confidence="EXTRACTED",
               confidence_score=1.0, source_file="repo1/a.py", weight=1.0)

    communities = {0: ["x", "y"]}
    cohesion = {0: 0.5}
    labels = {0: "Misc"}
    gods = []
    surprises = [
        {
            "source": "Alpha",
            "target": "Beta",
            "relation": "references",
            "confidence": "EXTRACTED",
            "source_files": ["repo1/a.py", "repo2/b.py"],
            "why": "cross-file connection",
        }
    ]
    detection = {"total_files": 2, "total_words": 200, "needs_graph": True, "warning": None}
    tokens = {"input": 50, "output": 25}
    report = generate(G, communities, cohesion, labels, gods, surprises, detection, tokens, "./project")
    assert "[semantically similar]" not in report
