"""Regression tests for GQLDB Python SDK.

API contract verification and backward compatibility tests.
Run with: pytest tests/regression/ -v
"""

import os
import pytest

from gqldb.config import ConfigBuilder, GqldbConfig
from gqldb.client import GqldbClient
from gqldb.types import TypedValue
from gqldb.errors import GqldbError


# Test configuration
HOST = os.getenv("GQLDB_HOST", "192.168.1.104:60061")
USERNAME = os.getenv("GQLDB_USERNAME", "admin")
PASSWORD = os.getenv("GQLDB_PASSWORD", "root11")
GRAPH = os.getenv("GQLDB_TEST_GRAPH", "miniCircle")


@pytest.fixture(scope="module")
def client():
    """Create and yield a connected client for the module."""
    config = (
        ConfigBuilder()
        .hosts(HOST)
        .username(USERNAME)
        .password(PASSWORD)
        .default_graph(GRAPH)
        .timeout(30)
        .build()
    )
    c = GqldbClient(config)
    try:
        c.login(USERNAME, PASSWORD)
    except GqldbError as e:
        if "authentication is not enabled" not in str(e).lower():
            raise
    yield c
    c.close()


# ==================== Response Structure Tests ====================


@pytest.mark.regression
class TestResponseStructure:
    """Tests for response data structure contracts."""

    def test_query_response_structure(self, client):
        """Verify query response has expected fields."""
        response = client.gql("RETURN 1 AS value")

        # Verify response is not None
        assert response is not None

        # Verify response has data
        assert not response.is_empty()

        # Verify we can iterate
        count = 0
        for row in response:
            assert row is not None
            count += 1
        assert count > 0

    def test_node_structure(self, client):
        """Verify node data structure contains expected fields."""
        response = client.gql("MATCH (n) RETURN n LIMIT 1")

        if response.is_empty():
            pytest.skip("No nodes in database to test structure")

        nodes, schemas = response.alias("n").as_nodes()
        if len(nodes) == 0:
            pytest.skip("No nodes returned")

        node = nodes[0]

        # Verify node has ID
        assert hasattr(node, 'id') or 'id' in node.__dict__ or hasattr(node, '_id')

        # Verify node has labels
        assert hasattr(node, 'labels') or hasattr(node, '_labels')

        # Verify node has properties
        assert hasattr(node, 'properties') or hasattr(node, '_properties')

    def test_edge_structure(self, client):
        """Verify edge data structure contains expected fields."""
        response = client.gql("MATCH ()-[e]->() RETURN e LIMIT 1")

        if response.is_empty():
            pytest.skip("No edges in database to test structure")

        edges, schemas = response.alias("e").as_edges()
        if len(edges) == 0:
            pytest.skip("No edges returned")

        edge = edges[0]

        # Verify edge has ID
        assert hasattr(edge, 'id') or 'id' in edge.__dict__ or hasattr(edge, '_id')

        # Verify edge has type
        assert hasattr(edge, 'type') or hasattr(edge, '_type') or hasattr(edge, 'label')

    def test_path_structure(self, client):
        """Verify path data structure."""
        response = client.gql("MATCH p = (n)-[e]->(m) RETURN p LIMIT 1")

        if response.is_empty():
            pytest.skip("No paths in database to test structure")

        paths = response.alias("p").as_paths()
        if len(paths) == 0:
            pytest.skip("No paths returned")

        path = paths[0]

        # Verify path has nodes
        assert hasattr(path, 'nodes') and len(path.nodes) > 0


# ==================== API Backward Compatibility Tests ====================


@pytest.mark.regression
class TestAPICompatibility:
    """Tests for API backward compatibility."""

    def test_config_builder_defaults(self):
        """Verify default configuration values."""
        config = ConfigBuilder().hosts(HOST).build()
        assert config is not None

    def test_config_builder_chaining(self):
        """Verify builder pattern chaining."""
        config = (
            ConfigBuilder()
            .hosts(HOST)
            .username(USERNAME)
            .password(PASSWORD)
            .default_graph(GRAPH)
            .timeout(30)
            .build()
        )
        assert config is not None

    def test_client_methods_exist(self, client):
        """Verify client has expected methods."""
        # Core methods
        assert hasattr(client, 'gql')
        assert hasattr(client, 'ping')
        assert hasattr(client, 'health_check')
        assert hasattr(client, 'begin_transaction')
        assert hasattr(client, 'close')

        # Auth methods
        assert hasattr(client, 'login')
        assert hasattr(client, 'logout')


# ==================== Protocol Compatibility Tests ====================


@pytest.mark.regression
class TestProtocolCompatibility:
    """Tests for protocol compatibility."""

    def test_session_id_propagation(self, client):
        """Verify session ID is maintained across requests."""
        # Execute multiple queries
        response1 = client.gql("RETURN 1")
        assert response1 is not None

        response2 = client.gql("RETURN 2")
        assert response2 is not None

    def test_transaction_id_handling(self, client):
        """Verify transaction ID is handled correctly."""
        tx = client.begin_transaction(GRAPH, read_only=True)
        assert tx is not None

        # Execute query within transaction
        from gqldb.client import QueryConfig
        response = client.gql("RETURN 1", config=QueryConfig(transaction_id=tx.id))
        assert response is not None

        # Rollback
        client.rollback(tx.id)


# ==================== Response Method Compatibility Tests ====================


@pytest.mark.regression
class TestResponseMethods:
    """Tests for response method compatibility."""

    def test_response_iteration_methods(self, client):
        """Verify all response iteration methods work."""
        response = client.gql("RETURN 1 AS a, 2 AS b, 3 AS c")

        # Test is_empty
        assert not response.is_empty()

        # Test first
        first = response.first()
        assert first is not None

        # Test last
        last = response.last()
        assert last is not None

        # Test iteration
        count = 0
        for row in response:
            count += 1
        assert count > 0

    def test_response_as_methods_exist(self, client):
        """Verify all As* methods are available via alias()."""
        response = client.gql("RETURN 1 AS value")

        # as_* methods are on AliasedResult, accessed via response.alias() or response.get()
        ar = response.alias("value")
        assert hasattr(ar, 'as_nodes')
        assert hasattr(ar, 'as_edges')
        assert hasattr(ar, 'as_table')
        assert hasattr(ar, 'as_attr')


# ==================== TypedValue Compatibility Tests ====================


@pytest.mark.regression
class TestTypedValueCompatibility:
    """Tests for TypedValue conversion compatibility."""

    @pytest.mark.parametrize("value,name", [
        (True, "bool_true"),
        (False, "bool_false"),
        (42, "int"),
        (3.14, "float"),
        ("hello", "string"),
        (None, "none"),
    ])
    def test_typed_value_conversions(self, value, name):
        """Verify TypedValue conversion methods."""
        tv = TypedValue.from_value(value)
        assert tv is not None

    def test_typed_value_methods_exist(self):
        """Verify TypedValue has expected methods."""
        tv = TypedValue.from_value(42)

        # TypedValue is a dataclass with type, data, is_null properties
        # Use to_python() to convert back to Python value
        assert hasattr(tv, 'type')
        assert hasattr(tv, 'data')
        assert hasattr(tv, 'is_null')
        assert hasattr(tv, 'to_python')

        # Convert back and verify
        result = tv.to_python()
        assert result == 42
