# type: ignore
"""Test FraiseQLRepository integration with where types and operator-based filtering.

This test suite ensures that the repository can handle where types generated by
safe_create_where_type, including operator-based filtering like {eq: value, gt: value}.
"""

import json
from datetime import UTC, date, datetime
from decimal import Decimal
from typing import AsyncGenerator, Optional
from uuid import UUID, uuid4

import pytest
import pytest_asyncio

pytestmark = pytest.mark.database

# Import database fixtures for this database test

from tests.fixtures.database.database_conftest import *  # noqa: F403
from tests.unit.utils.test_response_utils import extract_graphql_data

import fraiseql
from fraiseql.db import FraiseQLRepository, register_type_for_view
from fraiseql.sql.where_generator import safe_create_where_type


class TestRepositoryWhereIntegration:
    """Test suite for repository where type integration."""

    @pytest.fixture(scope="class")
    def test_types(self, clear_registry_class):
        """Create test types inside a fixture to prevent global state pollution.

        This fixture creates the Product and Order types along with their
        Where types. Using a fixture ensures the types are created fresh
        for each test and the clear_registry fixture handles cleanup.
        """
        @fraiseql.type
        class Product:
            id: UUID
            name: str
            price: Decimal
            stock: int
            created_at: datetime
            is_active: bool
            category: Optional[str] = None

        @fraiseql.type
        class Order:
            id: UUID
            product_id: UUID
            quantity: int
            total: Decimal
            order_date: date
            status: str

        ProductWhere = safe_create_where_type(Product)
        OrderWhere = safe_create_where_type(Order)

        return {
            "Product": Product,
            "Order": Order,
            "ProductWhere": ProductWhere,
            "OrderWhere": OrderWhere,
        }

    @pytest_asyncio.fixture(scope="class")
    async def setup_test_views(self, class_db_pool, test_schema, test_types) -> AsyncGenerator[None]:
        """Create test views with proper structure."""
        Product = test_types["Product"]
        Order = test_types["Order"]

        # Register types for views (for development mode)
        register_type_for_view("test_product_view", Product)
        register_type_for_view("test_order_view", Order)

        async with class_db_pool.connection() as conn:
            await conn.execute(f"SET search_path TO {test_schema}, public")
            # Create tables
            await conn.execute(
                """
                CREATE TABLE IF NOT EXISTS test_products (
                    id UUID PRIMARY KEY,
                    name TEXT NOT NULL,
                    price DECIMAL(10,2) NOT NULL,
                    stock INTEGER NOT NULL,
                    created_at TIMESTAMP WITH TIME ZONE NOT NULL,
                    is_active BOOLEAN NOT NULL,
                    category TEXT
                )
            """
            )

            await conn.execute(
                """
                CREATE TABLE IF NOT EXISTS test_orders (
                    id UUID PRIMARY KEY,
                    product_id UUID NOT NULL,
                    quantity INTEGER NOT NULL,
                    total DECIMAL(10,2) NOT NULL,
                    order_date DATE NOT NULL,
                    status TEXT NOT NULL
                )
            """
            )

            # Create views with JSONB data column
            await conn.execute(
                """
                CREATE OR REPLACE VIEW test_product_view AS
                SELECT
                    id, name, price, stock, created_at, is_active, category,
                    jsonb_build_object(
                        'id', id,
                        'name', name,
                        'price', price,
                        'stock', stock,
                        'created_at', created_at,
                        'is_active', is_active,
                        'category', category
                    ) as data
                FROM test_products
            """
            )

            await conn.execute(
                """
                CREATE OR REPLACE VIEW test_order_view AS
                SELECT
                    id, product_id, quantity, total, order_date, status,
                    jsonb_build_object(
                        'id', id,
                        'product_id', product_id,
                        'quantity', quantity,
                        'total', total,
                        'order_date', order_date,
                        'status', status
                    ) as data
                FROM test_orders
            """
            )

            # Insert test data
            product_id = uuid4()
            await conn.execute(
                """
                INSERT INTO test_products (id, name, price, stock, created_at, is_active, category)
                VALUES
                    (%s, 'Widget A', 19.99, 100, '2024-01-01 10:00:00+00', true, 'widgets'),
                    (%s, 'Widget B', 29.99, 50, '2024-01-02 10:00:00+00', true, 'widgets'),
                    (%s, 'Gadget X', 99.99, 10, '2024-01-03 10:00:00+00', false, 'gadgets'),
                    (%s, 'Gadget Y', 149.99, 5, '2024-01-04 10:00:00+00', true, null)
            """,
                (uuid4(), uuid4(), uuid4(), product_id),
            )

            await conn.execute(
                """
                INSERT INTO test_orders (id, product_id, quantity, total, order_date, status)
                VALUES
                    (%s, %s, 2, 39.98, '2024-01-05', 'completed'),
                    (%s, %s, 1, 19.99, '2024-01-06', 'pending'),
                    (%s, %s, 3, 89.97, '2024-01-07', 'completed')
            """,
                (uuid4(), product_id, uuid4(), product_id, uuid4(), product_id),
            )

        yield
