"""GraphQL on RDF filter and lookup tests.

Tests GraphQL filter operations for RDF-like data model.

Supports equality, range (age_gt/age_lt suffixes), string, and compound filters.
"""

import time

import pytest

# Try to import grafeo
try:
    from grafeo import GrafeoDB

    GRAFEO_AVAILABLE = True
except ImportError:
    GRAFEO_AVAILABLE = False


class TestRDFGraphQLFilters:
    """GraphQL filter tests for RDF-like data model."""

    def setup_method(self):
        """Create a fresh database."""
        self.db = GrafeoDB()

    def _execute_graphql(self, query: str):
        """Execute GraphQL query, skip if not supported."""
        try:
            return self.db.execute_graphql(query)
        except AttributeError:
            pytest.skip("GraphQL support not available")
            return None
        except NotImplementedError:
            pytest.skip("GraphQL not implemented")
            return None

    def _setup_person_data(self, count: int = 100):
        """Create RDF-like Person resources."""
        cities = ["NYC", "LA", "Chicago", "Boston", "Utrecht"]

        for i in range(count):
            self.db.create_node(
                ["Resource", "Person"],
                {
                    "uri": f"http://example.org/person/person{i}",
                    "name": f"Person{i}",
                    "age": i % 100,
                    "city": cities[i % len(cities)],
                },
            )

    # ===== Filter Correctness Tests =====

    def test_filter_equality_basic(self):
        """Test GraphQL filter with equality."""
        self._setup_person_data(100)

        result = self._execute_graphql(
            """
            query {
                person(age: 25) {
                    name
                    age
                }
            }
            """
        )
        rows = list(result)
        assert len(rows) >= 1, "Should find at least 1 person with age 25"

    def test_filter_range_basic(self):
        """Test GraphQL filter with range comparison."""
        self._setup_person_data(100)

        result = self._execute_graphql(
            """
            query {
                person(age_gt: 20, age_lt: 30) {
                    name
                    age
                }
            }
            """
        )
        rows = list(result)
        assert len(rows) >= 1, "Should find persons in age range"

    def test_filter_string_equality(self):
        """Test GraphQL filter with string comparison."""
        self._setup_person_data(100)

        result = self._execute_graphql(
            """
            query {
                person(city: "NYC") {
                    name
                    city
                }
            }
            """
        )
        rows = list(result)
        assert len(rows) >= 1, "Should find persons in NYC"

    def test_filter_compound_and(self):
        """Test GraphQL filter with compound AND condition."""
        self._setup_person_data(100)

        result = self._execute_graphql(
            """
            query {
                person(city: "NYC", age_gt: 50) {
                    name
                    city
                    age
                }
            }
            """
        )
        rows = list(result)
        # Results depend on data distribution
        assert isinstance(rows, list), "Should return a list of results"

    def test_filter_by_uri(self):
        """Test GraphQL filter by RDF URI."""
        self._setup_person_data(10)

        result = self._execute_graphql(
            """
            query {
                resource(uri: "http://example.org/person/person5") {
                    uri
                    name
                }
            }
            """
        )
        rows = list(result)
        assert len(rows) == 1, "Should find exactly 1 resource by URI"

    def test_filter_with_relationship(self):
        """Test GraphQL filter with relationship traversal."""
        alix = self.db.create_node(
            ["Resource", "Person"],
            {"uri": "http://example.org/alix", "name": "Alix", "age": 30},
        )
        gus = self.db.create_node(
            ["Resource", "Person"],
            {"uri": "http://example.org/gus", "name": "Gus", "age": 25},
        )
        vincent = self.db.create_node(
            ["Resource", "Person"],
            {"uri": "http://example.org/vincent", "name": "Vincent", "age": 35},
        )

        self.db.create_edge(alix.id, gus.id, "knows", {})
        self.db.create_edge(alix.id, vincent.id, "knows", {})

        result = self._execute_graphql(
            """
            query {
                person(name: "Alix") {
                    name
                    knows(age_gt: 30) {
                        name
                        age
                    }
                }
            }
            """
        )
        rows = list(result)
        assert len(rows) >= 1, "Should find Alix with her friends over 30"

    # ===== Direct Lookup Tests =====

    def test_get_node_by_id(self):
        """Test direct node lookup by ID."""
        node = self.db.create_node(
            ["Resource", "Person"],
            {"uri": "http://example.org/test", "name": "Test"},
        )

        retrieved = self.db.get_node(node.id)
        assert retrieved is not None, "get_node should return the node"
        assert retrieved.id == node.id, "Node ID should match"
        assert "Person" in retrieved.labels, "Node should have Person label"

    def test_get_node_nonexistent(self):
        """Test get_node returns None for nonexistent ID."""
        result = self.db.get_node(999999999)
        assert result is None, "get_node should return None for nonexistent node"

    # ===== Filter Performance Tests =====

    def test_filter_equality_performance(self):
        """Filter equality should complete quickly on 1K nodes."""
        self._setup_person_data(1000)

        # Warm up
        self._execute_graphql(
            """
            query { person(age: 50) { name } }
            """
        )

        # Time the filter
        start = time.perf_counter()
        for _ in range(10):
            result = self._execute_graphql(
                """
                query {
                    person(age: 50) {
                        name
                        age
                    }
                }
                """
            )
            list(result)
        elapsed = time.perf_counter() - start

        assert elapsed < 1.0, f"10 GraphQL filters took {elapsed:.3f}s, expected < 1.0s"

    def test_filter_range_performance(self):
        """Filter range should complete quickly on 1K nodes."""
        self._setup_person_data(1000)

        start = time.perf_counter()
        for _ in range(10):
            result = self._execute_graphql(
                """
                query {
                    person(age_gt: 20, age_lt: 40) {
                        name
                        age
                    }
                }
                """
            )
            list(result)
        elapsed = time.perf_counter() - start

        assert elapsed < 1.0, f"10 GraphQL range filters took {elapsed:.3f}s, expected < 1.0s"

    def test_direct_lookup_performance(self):
        """Direct lookup should be very fast."""
        nodes = []
        for i in range(1000):
            node = self.db.create_node(
                ["Resource"],
                {"uri": f"http://example.org/resource{i}", "index": i},
            )
            nodes.append(node.id)

        # Time 1000 direct lookups
        start = time.perf_counter()
        for node_id in nodes:
            node = self.db.get_node(node_id)
            assert node is not None
        elapsed = time.perf_counter() - start

        assert elapsed < 0.5, f"1000 direct lookups took {elapsed:.3f}s, expected < 0.5s"
