"""Tests for ConceptGraph navigation methods: top_concepts, neighbors_of, shortest_path, neighborhood."""

import sqlite3
import pytest
from neo_cortex.graph import ConceptGraph


@pytest.fixture
def graph():
    conn = sqlite3.connect(":memory:")
    g = ConceptGraph(conn)
    # Build a test graph:
    # A(3) -- B(2) -- C(3) -- D(1)
    #   \              |
    #    E(1)         F(1)
    g.add_memory("m1", ["A", "B", "E"])
    g.add_memory("m2", ["A", "B", "C"])
    g.add_memory("m3", ["A", "C", "F"])
    g.add_memory("m4", ["C", "D"])
    return g


class TestTopConcepts:
    def test_returns_sorted_by_mentions(self, graph):
        result = graph.top_concepts(n=3)
        assert len(result) == 3
        assert result[0]["concept"] in ("A", "C")
        assert result[0]["mentions"] == 3

    def test_respects_n_limit(self, graph):
        result = graph.top_concepts(n=2)
        assert len(result) == 2

    def test_empty_graph(self):
        conn = sqlite3.connect(":memory:")
        g = ConceptGraph(conn)
        assert g.top_concepts(n=10) == []

    def test_includes_edge_count(self, graph):
        result = graph.top_concepts(n=1)
        assert "edges" in result[0]
        assert result[0]["edges"] > 0

    def test_includes_memory_count(self, graph):
        result = graph.top_concepts(n=1)
        assert "memories" in result[0]
        assert result[0]["memories"] > 0


class TestNeighborsOf:
    def test_returns_neighbors_with_weights(self, graph):
        result = graph.neighbors_of("A")
        names = [r["concept"] for r in result]
        assert "B" in names
        assert "E" in names

    def test_sorted_by_weight_desc(self, graph):
        result = graph.neighbors_of("A")
        weights = [r["weight"] for r in result]
        assert weights == sorted(weights, reverse=True)

    def test_unknown_concept_returns_empty(self, graph):
        assert graph.neighbors_of("UNKNOWN") == []

    def test_includes_neighbor_mentions(self, graph):
        result = graph.neighbors_of("A")
        for r in result:
            assert "mentions" in r


class TestShortestPath:
    def test_direct_neighbors(self, graph):
        path = graph.shortest_path("A", "B")
        assert path == ["A", "B"]

    def test_multi_hop(self, graph):
        path = graph.shortest_path("E", "D")
        assert path[0] == "E"
        assert path[-1] == "D"
        assert len(path) >= 3

    def test_no_path_returns_empty(self):
        """Isolated nodes (single-concept memories) have no edges between them."""
        conn = sqlite3.connect(":memory:")
        g = ConceptGraph(conn)
        g.add_memory("m1", ["X"])
        g.add_memory("m2", ["Y"])
        assert g.shortest_path("X", "Y") == []

    def test_unknown_concept_returns_empty(self, graph):
        assert graph.shortest_path("A", "UNKNOWN") == []

    def test_same_concept(self, graph):
        path = graph.shortest_path("A", "A")
        assert path == ["A"]


class TestNeighborhood:
    def test_depth_1(self, graph):
        result = graph.neighborhood("A", depth=1)
        assert "A" in [n["concept"] for n in result["nodes"]]
        node_names = {n["concept"] for n in result["nodes"]}
        assert "B" in node_names
        assert "E" in node_names

    def test_depth_2_expands(self, graph):
        result = graph.neighborhood("E", depth=2)
        node_names = {n["concept"] for n in result["nodes"]}
        # E → A → B, C, F (depth 2)
        assert "E" in node_names
        assert "A" in node_names
        assert len(node_names) > 2

    def test_includes_edges(self, graph):
        result = graph.neighborhood("A", depth=1)
        assert len(result["edges"]) > 0
        for e in result["edges"]:
            assert "source" in e
            assert "target" in e
            assert "weight" in e

    def test_unknown_concept(self, graph):
        result = graph.neighborhood("UNKNOWN", depth=1)
        assert result["nodes"] == []
        assert result["edges"] == []
