"""Integration tests for as_* methods and printers."""

import pytest
import uuid

from gqldb.client import GqldbClient, QueryConfig
from gqldb.types import GraphType, NodeData, EdgeData
from gqldb.printers import (
    print_nodes,
    print_edges,
    print_paths,
    print_any,
    print_nodes_without_schema,
    print_edges_without_schema,
)
from tests.conftest import drop_test_graph


@pytest.mark.integration
class TestAsMethodsIntegration:
    """Integration tests for as_* methods."""

    @pytest.fixture
    def test_graph(self, connected_client: GqldbClient, request):
        """Create a temporary test graph."""
        graph_name = f"test_as_{request.node.name}_{uuid.uuid4().hex[:8]}"
        connected_client.create_graph(graph_name, GraphType.OPEN, "Test as methods")
        yield graph_name
        drop_test_graph(connected_client, graph_name)

    def test_as_nodes_with_real_data(self, connected_client: GqldbClient, test_graph: str):
        """Test as_nodes() extracts nodes from real query results."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            # Insert test nodes
            nodes = [
                NodeData(labels=["Person"], properties={"name": "Alice", "age": 30}),
                NodeData(labels=["Person"], properties={"name": "Bob", "age": 25}),
            ]
            result = connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )
            assert result.success

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Query and extract nodes - use RETURN n to get PropertyType.NODE typed values
        resp = connected_client.gql(
            "MATCH (n:Person) RETURN n",
            QueryConfig(graph_name=test_graph)
        )

        # Extract nodes from column "n"
        ar = resp.alias("n")
        extracted_nodes, schemas = ar.as_nodes()

        assert len(extracted_nodes) >= 2

        # Verify some data was extracted
        names = [n.properties.get("name") for n in extracted_nodes if n.properties.get("name")]
        assert "Alice" in names
        assert "Bob" in names

        # Test printer
        output = print_nodes(extracted_nodes, schemas)
        print(f"Printed nodes:\n{output}")
        assert "node(s)" in output

    def test_as_edges_with_real_data(self, connected_client: GqldbClient, test_graph: str):
        """Test as_edges() extracts edges from real query results."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            # Insert nodes first
            nodes = [
                NodeData(labels=["EdgeTestPerson"], properties={"name": "Dave"}),
                NodeData(labels=["EdgeTestPerson"], properties={"name": "Eve"}),
            ]
            node_result = connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )
            assert node_result.success

            # Query to get node IDs
            query_config = QueryConfig(graph_name=test_graph)
            resp = connected_client.gql(
                "MATCH (n:EdgeTestPerson) RETURN id(n) AS node_id ORDER BY n.name",
                query_config
            )

            node_ids = []
            for i in range(resp.row_count):
                row = resp.rows[i]
                value = resp.get_by_name(row, "node_id")
                if value:
                    node_ids.append(value)

            if len(node_ids) >= 2:
                # Insert edge
                edges = [
                    EdgeData(
                        label="KNOWS_TEST",
                        from_node_id=node_ids[0],
                        to_node_id=node_ids[1],
                        properties={"since": 2024, "strength": 0.8}
                    )
                ]
                edge_result = connected_client.insert_edges(
                    test_graph,
                    edges,
                    bulk_import_session_id=session.session_id
                )
                assert edge_result.success

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Query and extract edges - use RETURN e to get PropertyType.EDGE typed values
        resp = connected_client.gql(
            "MATCH (a)-[e:KNOWS_TEST]->(b) RETURN e",
            QueryConfig(graph_name=test_graph)
        )

        # Extract edges from column "e"
        ar = resp.alias("e")
        extracted_edges, schemas = ar.as_edges()

        assert len(extracted_edges) >= 1

        # Test printer
        output = print_edges(extracted_edges, schemas)
        print(f"Printed edges:\n{output}")
        assert "edge(s)" in output

    def test_as_table_with_real_data(self, connected_client: GqldbClient, test_graph: str):
        """Test as_table() converts query results to table."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            # Insert test nodes
            nodes = [
                NodeData(labels=["TableTest"], properties={"val": 1}),
                NodeData(labels=["TableTest"], properties={"val": 2}),
            ]
            connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Query and get table
        resp = connected_client.gql(
            "MATCH (n:TableTest) RETURN n.val as value ORDER BY n.val",
            QueryConfig(graph_name=test_graph)
        )

        # Extract table from column "value"
        ar = resp.alias("value")
        table = ar.as_table()

        assert len(table.headers) >= 1
        print(f"Table headers: {[h.name for h in table.headers]}")
        print(f"Table rows: {len(table.rows)}")

    def test_as_attr_with_real_data(self, connected_client: GqldbClient, test_graph: str):
        """Test as_attr() extracts attribute values."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            # Insert test nodes
            nodes = [
                NodeData(labels=["AttrTest"], properties={"name": "Alice"}),
                NodeData(labels=["AttrTest"], properties={"name": "Bob"}),
                NodeData(labels=["AttrTest"], properties={"name": "Charlie"}),
            ]
            connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Query and extract attribute
        resp = connected_client.gql(
            "MATCH (n:AttrTest) RETURN n.name as name",
            QueryConfig(graph_name=test_graph)
        )

        if len(resp) > 0:
            # Extract attribute from column "name"
            ar = resp.alias("name")
            attr = ar.as_attr()
            assert attr.name == "name"
            assert len(attr.values) >= 1
            print(f"Extracted names: {attr.values}")

    def test_as_attr_column_not_found(self, connected_client: GqldbClient, test_graph: str):
        """Test alias() throws for non-existent column."""
        resp = connected_client.gql("MATCH (n) RETURN n.name as name LIMIT 1", QueryConfig(graph_name=test_graph))

        with pytest.raises(KeyError, match="Column alias not found"):
            resp.alias("nonexistent")


@pytest.mark.integration
class TestPrintAnyIntegration:
    """Integration tests for print_any function."""

    @pytest.fixture
    def test_graph(self, connected_client: GqldbClient, request):
        """Create a temporary test graph."""
        graph_name = f"test_print_{request.node.name}_{uuid.uuid4().hex[:8]}"
        connected_client.create_graph(graph_name, GraphType.OPEN, "Test print_any")
        yield graph_name
        drop_test_graph(connected_client, graph_name)

    def test_auto_detect_nodes(self, connected_client: GqldbClient, test_graph: str):
        """Test print_any() auto-detects node response."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            nodes = [NodeData(labels=["PrintAnyTest"], properties={"val": 1})]
            connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Use RETURN n to get PropertyType.NODE typed values
        resp = connected_client.gql(
            "MATCH (n:PrintAnyTest) RETURN n",
            QueryConfig(graph_name=test_graph)
        )

        output = print_any(resp)
        assert output is not None
        print(f"Auto-detected (nodes):\n{output}")

    def test_auto_detect_edges(self, connected_client: GqldbClient, test_graph: str):
        """Test print_any() auto-detects edge response."""
        # Start bulk import session
        session = connected_client.start_bulk_import(test_graph)

        try:
            nodes = [
                NodeData(labels=["PANode"], properties={"name": "A"}),
                NodeData(labels=["PANode"], properties={"name": "B"}),
            ]
            node_result = connected_client.insert_nodes(
                test_graph,
                nodes,
                bulk_import_session_id=session.session_id
            )

            # Query to get node IDs
            query_config = QueryConfig(graph_name=test_graph)
            resp = connected_client.gql(
                "MATCH (n:PANode) RETURN id(n) AS node_id ORDER BY n.name",
                query_config
            )

            node_ids = []
            for i in range(resp.row_count):
                row = resp.rows[i]
                value = resp.get_by_name(row, "node_id")
                if value:
                    node_ids.append(value)

            if len(node_ids) >= 2:
                edges = [
                    EdgeData(
                        label="PA_LINK",
                        from_node_id=node_ids[0],
                        to_node_id=node_ids[1],
                        properties={}
                    )
                ]
                connected_client.insert_edges(
                    test_graph,
                    edges,
                    bulk_import_session_id=session.session_id
                )

        finally:
            connected_client.end_bulk_import(session.session_id)

        # Use RETURN e to get PropertyType.EDGE typed values
        resp = connected_client.gql(
            "MATCH (a)-[e:PA_LINK]->(b) RETURN e",
            QueryConfig(graph_name=test_graph)
        )

        output = print_any(resp)
        assert output is not None
        print(f"Auto-detected (edges):\n{output}")

    def test_fallback_to_table(self, connected_client: GqldbClient, test_graph: str):
        """Test print_any() falls back to table format."""
        resp = connected_client.gql("RETURN 1 + 1 as result, 'hello' as greeting", QueryConfig(graph_name=test_graph))

        output = print_any(resp)
        assert output is not None
        print(f"Auto-detected (table):\n{output}")

    def test_empty_response(self, connected_client: GqldbClient, test_graph: str):
        """Test print_any() handles empty response."""
        resp = connected_client.gql(
            "MATCH (n:NonExistentLabel12345) RETURN n LIMIT 1",
            QueryConfig(graph_name=test_graph)
        )

        output = print_any(resp)
        assert "No data" in output
