"""
Tests for django_odata.utils module.
"""

import pytest

# Test model for utility tests
from django.db import models
from django.http import QueryDict
from django.test import TestCase

from django_odata.utils import ODataQueryBuilder, parse_odata_query


class UtilsTestModel(models.Model):
    """Test model for utility tests."""

    name = models.CharField(max_length=100)
    value = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        app_label = "tests"


class TestParseODataQuery(TestCase):
    """Test OData query parameter parsing."""

    def test_parse_basic_odata_params(self):
        """Test parsing basic OData query parameters."""
        query_params = QueryDict(
            '$filter=name eq "test"&$orderby=value desc&$top=10&$skip=5'
        )
        result = parse_odata_query(query_params)

        expected = {
            "$filter": 'name eq "test"',
            "$orderby": "value desc",
            "$top": "10",
            "$skip": "5",
        }
        self.assertEqual(result, expected)

    def test_parse_legacy_params(self):
        """Test parsing legacy parameters (only omit is supported)."""
        query_params = QueryDict("omit=created_at&$select=name,value")
        result = parse_odata_query(query_params)

        expected = {"$select": "name,value", "omit": "created_at"}
        self.assertEqual(result, expected)

    def test_parse_mixed_params(self):
        """Test parsing mix of OData and legacy parameters."""
        query_params = QueryDict(
            "$filter=value gt 10&$select=name,value&$orderby=name asc"
        )
        result = parse_odata_query(query_params)

        expected = {
            "$filter": "value gt 10",
            "$orderby": "name asc",
            "$select": "name,value",
        }
        self.assertEqual(result, expected)

    def test_parse_empty_params(self):
        """Test parsing empty query parameters."""
        query_params = QueryDict("")
        result = parse_odata_query(query_params)
        self.assertEqual(result, {})

    def test_parse_dict_params(self):
        """Test parsing regular dictionary parameters."""
        query_params = {"$filter": 'name eq "test"', "$top": "5", "$select": "name"}
        result = parse_odata_query(query_params)
        self.assertEqual(result, query_params)


class TestApplyODataQueryParams(TestCase):
    """Test applying OData query parameters to QuerySets."""

    def setUp(self):
        """Set up test data."""

        # Create a mock queryset-like object for testing
        class MockQuerySet:
            def __init__(self, data=None):
                self.data = data or []
                self._filters = []
                self._order_by = []
                self._limit = None
                self._offset = None

            def filter(self, **kwargs):
                new_qs = MockQuerySet(self.data)
                new_qs._filters = self._filters + [kwargs]
                return new_qs

            def order_by(self, *fields):
                new_qs = MockQuerySet(self.data)
                new_qs._order_by = list(fields)
                return new_qs

            def __getitem__(self, key):
                new_qs = MockQuerySet(self.data)
                if isinstance(key, slice):
                    new_qs._offset = key.start
                    new_qs._limit = key.stop
                return new_qs

        self.mock_queryset = MockQuerySet()

    def test_apply_orderby_asc(self):
        """Test applying $orderby with ascending order."""
        params = {"$orderby": "name asc"}
        # Note: This test would need a real queryset to work properly
        # For now, we'll test the parameter parsing logic
        self.assertIn("$orderby", params)

    def test_apply_orderby_desc(self):
        """Test applying $orderby with descending order."""
        params = {"$orderby": "value desc"}
        # Test the orderby parsing logic
        order_fields = []
        for field in params["$orderby"].split(","):
            field = field.strip()
            if field.endswith(" desc"):
                order_fields.append("-" + field[:-5].strip())
            elif field.endswith(" asc"):
                order_fields.append(field[:-4].strip())
            else:
                order_fields.append(field)

        self.assertEqual(order_fields, ["-value"])

    def test_apply_top_and_skip(self):
        """Test applying $top and $skip parameters."""
        params = {"$top": "10", "$skip": "5"}

        # Test parameter validation
        try:
            top = int(params["$top"])
            skip = int(params["$skip"])
            self.assertEqual(top, 10)
            self.assertEqual(skip, 5)
        except (ValueError, TypeError):
            self.fail("Should parse top and skip correctly")

    def test_invalid_top_skip_values(self):
        """Test handling of invalid $top and $skip values."""
        params = {"$top": "invalid", "$skip": "invalid"}

        # Should handle invalid values gracefully
        try:
            int(params["$top"])
        except ValueError:
            pass  # Expected

        try:
            int(params["$skip"])
        except ValueError:
            pass  # Expected


class TestODataQueryBuilder(TestCase):
    """Test OData query builder utility."""

    def test_basic_query_building(self):
        """Test basic query building functionality."""
        builder = ODataQueryBuilder()
        result = (
            builder.filter("name eq 'test'").order("value", desc=True).limit(10).build()
        )

        expected = {
            "$filter": "(name eq 'test')",
            "$orderby": "value desc",
            "$top": "10",
        }
        self.assertEqual(result, expected)

    def test_multiple_filters(self):
        """Test building queries with multiple filters."""
        builder = ODataQueryBuilder()
        result = builder.filter("name eq 'test'").filter("value gt 5").build()

        expected = {"$filter": "(name eq 'test') and (value gt 5)"}
        self.assertEqual(result, expected)

    def test_select_and_expand(self):
        """Test select and expand functionality."""
        builder = ODataQueryBuilder()
        result = builder.select("name", "value").expand("related", "other").build()

        expected = {"$select": "name,value", "$expand": "related,other"}
        self.assertEqual(result, expected)

    def test_complete_query(self):
        """Test building a complete query with all options."""
        builder = ODataQueryBuilder()
        result = (
            builder.filter("status eq 'active'")
            .order("created_at", desc=True)
            .limit(20)
            .offset(10)
            .select("id", "name", "status")
            .expand("author")
            .build()
        )

        expected = {
            "$filter": "(status eq 'active')",
            "$orderby": "created_at desc",
            "$top": "20",
            "$skip": "10",
            "$select": "id,name,status",
            "$expand": "author",
        }
        self.assertEqual(result, expected)

    def test_empty_query(self):
        """Test building an empty query."""
        builder = ODataQueryBuilder()
        result = builder.build()
        self.assertEqual(result, {})


class TestBuildODataMetadata(TestCase):
    """Test OData metadata building functionality."""

    def test_metadata_building_basic(self):
        """Test basic metadata building."""
        # This would require a real Django setup to test properly
        # For now, we'll test the structure
        metadata_structure = {
            "name": "TestModel",
            "namespace": "test_app",
            "properties": {},
            "navigation_properties": {},
        }

        required_keys = ["name", "namespace", "properties", "navigation_properties"]
        for key in required_keys:
            self.assertIn(key, metadata_structure)


if __name__ == "__main__":
    pytest.main([__file__])
