# atomsql/tests/test_date_field.py
import pytest
from datetime import date, datetime
from atomsql.orm.models import Model
from atomsql.orm.fields import StringField, DateField


class Milestone(Model):
    name = StringField()
    achieved_at = DateField(nullable=True)


def test_date_field_validation(db):
    """Ensure DateField only accepts date objects (not datetime)."""
    db.register(Milestone)

    # Valid date
    m = Milestone(name="Beta Launch", achieved_at=date(2026, 1, 1))
    assert m.achieved_at == date(2026, 1, 1)

    # Invalid types should raise ValueError
    with pytest.raises(ValueError, match="expected a date"):
        Milestone(name="Fail", achieved_at="test")  # String is not allowed

    with pytest.raises(ValueError, match="expected a date"):
        Milestone(name="Fail", achieved_at=12345678)  # Integer is not allowed


def test_date_field_rejects_datetime(db):
    """Ensure DateField rejects datetime objects (only date accepted)."""
    db.register(Milestone)

    # datetime objects should be rejected
    with pytest.raises(ValueError, match="expected a date"):
        Milestone(name="Fail", achieved_at=datetime(2026, 1, 1, 12, 30, 0))


def test_date_field_null_handling(db):
    """Ensure DateField handles None correctly when nullable=True."""
    db.register(Milestone)
    m = Milestone(name="Future Goal", achieved_at=None)
    m.save()

    retrieved = Milestone.objects().filter(name="Future Goal")[0]
    assert retrieved.achieved_at is None


def test_date_query_filtering(db):
    """Verify that date objects can be used in filters."""
    db.register(Milestone)
    Milestone(name="M1", achieved_at=date(2025, 12, 31)).save()
    Milestone(name="M2", achieved_at=date(2026, 1, 3)).save()

    # Exact match
    res = Milestone.objects().filter(achieved_at=date(2026, 1, 3))[0]
    assert res.name == "M2"

    # Comparison (using SQLite string sorting logic)
    # Find milestones achieved after New Years Eve
    after_new_year = list(
        Milestone.objects().filter(achieved_at__gt=date(2025, 12, 31))
    )
    assert len(after_new_year) == 1
    assert after_new_year[0].name == "M2"


def test_backend_preparation_logic(db):
    """
    Directly test the backend's prepare_value to ensure
    it converts date objects to ISO strings for SQLite.
    """
    test_date = date(2026, 5, 20)
    # This simulates what the SQLBuilder does
    prepared = db.backend.prepare_value(test_date)

    assert isinstance(prepared, str)
    assert prepared == "2026-05-20"


def test_date_field_in_axiom_context(db):
    """Integration test: Check if exists() works with DateFields."""
    db.register(Milestone)
    Milestone(name="Audit", achieved_at=date(2026, 1, 3)).save()

    # Verify existence using a date object
    exists = Milestone.objects().filter(achieved_at=date(2026, 1, 3)).exists()
    assert exists is True
