"""Integration tests for bulk ingest and export — requires a running PostgreSQL."""

from __future__ import annotations

import pandas as pd
import pytest

pytestmark = pytest.mark.integration


class TestLoadNodes:
    def test_create_mode(self, fresh_graph):
        db = fresh_graph
        df = pd.DataFrame([
            {"name": "Alice", "age": 30},
            {"name": "Bob", "age": 25},
            {"name": "Carol", "age": 35},
        ])
        count = db.load_nodes(df, "Person")
        assert count == 3

        result = db.cypher("MATCH (n:Person) RETURN n.name ORDER BY n.name")
        names = [r["name"] for r in result]
        assert names == ["Alice", "Bob", "Carol"]

    def test_merge_mode(self, fresh_graph):
        db = fresh_graph
        df1 = pd.DataFrame([
            {"uid": "a1", "name": "Alice", "age": 30},
            {"uid": "b1", "name": "Bob", "age": 25},
        ])
        db.load_nodes(df1, "Person", id_col="uid")

        # Second load — should update, not duplicate
        df2 = pd.DataFrame([
            {"uid": "a1", "name": "Alice", "age": 31},
            {"uid": "c1", "name": "Carol", "age": 35},
        ])
        count = db.load_nodes(df2, "Person", id_col="uid")
        assert count == 2

        result = db.cypher("MATCH (n:Person) RETURN n.name ORDER BY n.name")
        names = [r["name"] for r in result]
        assert names == ["Alice", "Bob", "Carol"]

    def test_property_cols_filter(self, fresh_graph):
        db = fresh_graph
        df = pd.DataFrame([
            {"name": "Alice", "age": 30, "secret": "x"},
        ])
        db.load_nodes(df, "Person", property_cols=["name", "age"])

        result = db.cypher("MATCH (n:Person) RETURN n")
        node = result.single()["n"]
        assert "name" in node.properties
        assert "secret" not in node.properties

    def test_empty_dataframe(self, fresh_graph):
        db = fresh_graph
        df = pd.DataFrame(columns=["name", "age"])
        count = db.load_nodes(df, "Person")
        assert count == 0

    def test_custom_batch_size(self, fresh_graph):
        db = fresh_graph
        df = pd.DataFrame([{"name": f"p{i}"} for i in range(10)])
        count = db.load_nodes(df, "Person", batch_size=3)
        assert count == 10


class TestLoadEdges:
    def test_basic_edge_load(self, fresh_graph):
        db = fresh_graph
        # Create nodes first
        nodes = pd.DataFrame([
            {"name": "Alice"},
            {"name": "Bob"},
            {"name": "Carol"},
        ])
        db.load_nodes(nodes, "Person")

        edges = pd.DataFrame([
            {"name": "Alice", "friend": "Bob"},
            {"name": "Bob", "friend": "Carol"},
        ])
        count = db.load_edges(
            edges, "KNOWS",
            source_col="name", source_label="Person",
            target_col="friend", target_label="Person",
            target_key="name",
        )
        assert count == 2

        result = db.cypher("MATCH ()-[r:KNOWS]->() RETURN count(r) AS cnt")
        assert result.single()["cnt"] == 2

    def test_edges_with_properties(self, fresh_graph):
        db = fresh_graph
        nodes = pd.DataFrame([{"name": "Alice"}, {"name": "Bob"}])
        db.load_nodes(nodes, "Person")

        edges = pd.DataFrame([
            {"name": "Alice", "friend": "Bob", "since": 2020, "weight": 0.9},
        ])
        count = db.load_edges(
            edges, "KNOWS",
            source_col="name", source_label="Person",
            target_col="friend", target_label="Person",
            target_key="name",
            property_cols=["since", "weight"],
        )
        assert count == 1

        result = db.cypher("MATCH ()-[r:KNOWS]->() RETURN r")
        edge = result.single()["r"]
        assert edge.properties["since"] == 2020

    def test_empty_edges(self, fresh_graph):
        db = fresh_graph
        df = pd.DataFrame(columns=["src", "tgt"])
        count = db.load_edges(
            df, "KNOWS",
            source_col="src", source_label="Person",
            target_col="tgt", target_label="Person",
        )
        assert count == 0


class TestExportNodes:
    def test_export_roundtrip(self, fresh_graph):
        db = fresh_graph
        df_in = pd.DataFrame([
            {"name": "Alice", "age": 30},
            {"name": "Bob", "age": 25},
        ])
        db.load_nodes(df_in, "Person")
        df_out = db.export_nodes("Person")
        assert len(df_out) == 2
        assert "n._label" in df_out.columns
        assert "n.name" in df_out.columns

    def test_export_with_limit(self, fresh_graph):
        db = fresh_graph
        df_in = pd.DataFrame([{"name": f"p{i}"} for i in range(5)])
        db.load_nodes(df_in, "Person")
        df_out = db.export_nodes("Person", limit=2)
        assert len(df_out) == 2

    def test_export_with_property_filter(self, fresh_graph):
        db = fresh_graph
        df_in = pd.DataFrame([{"name": "Alice", "age": 30}])
        db.load_nodes(df_in, "Person")
        df_out = db.export_nodes("Person", properties=["name"])
        assert "n.name" in df_out.columns
        assert "n.age" not in df_out.columns


class TestExportEdges:
    def test_export_edges(self, fresh_graph):
        db = fresh_graph
        nodes = pd.DataFrame([{"name": "Alice"}, {"name": "Bob"}])
        db.load_nodes(nodes, "Person")
        edges = pd.DataFrame([
            {"name": "Alice", "friend": "Bob", "since": 2020},
        ])
        db.load_edges(
            edges, "KNOWS",
            source_col="name", source_label="Person",
            target_col="friend", target_label="Person",
            target_key="name",
            property_cols=["since"],
        )
        df_out = db.export_edges("KNOWS")
        assert len(df_out) == 1
        assert "r._label" in df_out.columns
