"""
Tests for wosecopy.graph module.
"""
import pytest
import networkx as nx
from pathlib import Path
from wosecopy.graph import (
    build_graph,
    build_graphs_from_list,
    export_graph,
    export_graphs,
    load_graph,
    merge_graphs,
    get_graph_summary,
)


class TestBuildGraph:
    """Test graph building functionality."""

    def test_build_empty_graph(self):
        """Test building graph with empty concept list."""
        graph = build_graph([])
        assert graph.number_of_nodes() == 0
        assert graph.number_of_edges() == 0

    def test_build_single_concept(self):
        """Test building graph with single concept."""
        graph = build_graph(['belief'])
        assert graph.number_of_nodes() == 1
        assert graph.number_of_edges() == 0

    def test_build_chain_graph(self):
        """Test building chain-type graph."""
        concepts = ['belief', 'faith', 'church', 'prayer']
        graph = build_graph(concepts, graph_type='chain')

        assert graph.number_of_nodes() == 4
        assert graph.number_of_edges() == 3  # Chain has n-1 edges
        assert graph.has_edge('belief', 'faith')
        assert graph.has_edge('faith', 'church')
        assert graph.has_edge('church', 'prayer')

    def test_build_dense_graph(self):
        """Test building dense-type graph."""
        concepts = ['belief', 'faith', 'church']
        graph = build_graph(concepts, graph_type='dense')

        assert graph.number_of_nodes() == 3
        # Dense graph connects all pairs: n*(n-1)/2
        assert graph.number_of_edges() == 3

    def test_build_graph_with_empty_strings(self):
        """Test graph building handles empty strings."""
        concepts = ['belief', '', 'faith', None, 'church']
        graph = build_graph(concepts, graph_type='chain')

        # Empty strings and None should be filtered
        assert 'belief' in graph.nodes()
        assert 'faith' in graph.nodes()
        assert 'church' in graph.nodes()
        assert '' not in graph.nodes()


class TestBuildGraphsFromList:
    """Test building multiple graphs."""

    def test_build_multiple_graphs(self):
        """Test building graphs from list of concept lists."""
        concept_lists = [
            ['a', 'b', 'c'],
            ['x', 'y'],
            ['one', 'two', 'three', 'four']
        ]

        graphs = build_graphs_from_list(concept_lists)

        assert len(graphs) == 3
        assert graphs[0].number_of_nodes() == 3
        assert graphs[1].number_of_nodes() == 2
        assert graphs[2].number_of_nodes() == 4


class TestExportGraph:
    """Test graph export functionality."""

    def test_export_graphml(self, tmp_path):
        """Test exporting to GraphML format."""
        graph = build_graph(['a', 'b', 'c'])
        output_file = tmp_path / "test.graphml"

        export_graph(graph, str(output_file), format='graphml')

        assert output_file.exists()

    def test_export_gexf(self, tmp_path):
        """Test exporting to GEXF format."""
        graph = build_graph(['a', 'b', 'c'])
        output_file = tmp_path / "test.gexf"

        export_graph(graph, str(output_file), format='gexf')

        assert output_file.exists()

    def test_export_creates_directory(self, tmp_path):
        """Test that export creates parent directories."""
        graph = build_graph(['a', 'b'])
        output_file = tmp_path / "subdir" / "graph.graphml"

        export_graph(graph, str(output_file), format='graphml')

        assert output_file.exists()

    def test_export_invalid_format(self, tmp_path):
        """Test that invalid format raises error."""
        graph = build_graph(['a', 'b'])
        output_file = tmp_path / "test.invalid"

        with pytest.raises(ValueError):
            export_graph(graph, str(output_file), format='invalid')


class TestExportGraphs:
    """Test exporting multiple graphs."""

    def test_export_multiple_graphs(self, tmp_path):
        """Test exporting multiple graphs to directory."""
        graphs = [
            build_graph(['a', 'b']),
            build_graph(['x', 'y', 'z'])
        ]

        export_graphs(graphs, str(tmp_path), prefix='test', format='graphml')

        files = list(tmp_path.glob('*.graphml'))
        assert len(files) == 2


class TestLoadGraph:
    """Test graph loading functionality."""

    def test_load_graphml(self, tmp_path):
        """Test loading GraphML file."""
        # Create and save graph
        original = build_graph(['a', 'b', 'c'], graph_type='chain')
        file_path = tmp_path / "test.graphml"
        export_graph(original, str(file_path), format='graphml')

        # Load graph
        loaded = load_graph(str(file_path), format='graphml')

        assert loaded.number_of_nodes() == original.number_of_nodes()
        assert loaded.number_of_edges() == original.number_of_edges()


class TestMergeGraphs:
    """Test graph merging functionality."""

    def test_merge_two_graphs(self):
        """Test merging two graphs."""
        g1 = build_graph(['a', 'b'], graph_type='chain')
        g2 = build_graph(['c', 'd'], graph_type='chain')

        merged = merge_graphs([g1, g2])

        assert merged.number_of_nodes() == 4
        assert 'a' in merged.nodes()
        assert 'd' in merged.nodes()

    def test_merge_overlapping_graphs(self):
        """Test merging graphs with overlapping nodes."""
        g1 = build_graph(['a', 'b', 'c'], graph_type='chain')
        g2 = build_graph(['b', 'c', 'd'], graph_type='chain')

        merged = merge_graphs([g1, g2])

        # Should have unique nodes
        assert merged.number_of_nodes() == 4  # a, b, c, d


class TestGraphSummary:
    """Test graph summary statistics."""

    def test_summary_empty_graph(self):
        """Test summary of empty graph."""
        graph = nx.Graph()
        summary = get_graph_summary(graph)

        assert summary['num_nodes'] == 0
        assert summary['num_edges'] == 0
        assert summary['is_connected'] == False
        assert summary['density'] == 0

    def test_summary_connected_graph(self):
        """Test summary of connected graph."""
        graph = build_graph(['a', 'b', 'c'], graph_type='chain')
        summary = get_graph_summary(graph)

        assert summary['num_nodes'] == 3
        assert summary['num_edges'] == 2
        assert summary['is_connected'] == True
        assert summary['num_components'] == 1
        assert summary['density'] > 0

    def test_summary_disconnected_graph(self):
        """Test summary of disconnected graph."""
        graph = nx.Graph()
        graph.add_edge('a', 'b')
        graph.add_edge('c', 'd')

        summary = get_graph_summary(graph)

        assert summary['num_nodes'] == 4
        assert summary['num_edges'] == 2
        assert summary['is_connected'] == False
        assert summary['num_components'] == 2
