import datetime

from django.test import TestCase
from django.utils import timezone
from rest_framework.test import APIRequestFactory

from config.filters import DateRangeFilter, QueryParamFilter
from inventory.models import Category, SparePart, Unit


class _View:
    date_field = 'created_at'
    query_filters = {'is_active': 'is_active'}


class ListFilterTests(TestCase):
    def setUp(self):
        self.unit, _ = Unit.objects.get_or_create(name='Piece', defaults={'abbreviation': 'pc'})
        self.old = SparePart.objects.create(
            name='Old', part_number='O-1', unit=self.unit, buying_price=1, selling_price=2, is_active=False,
        )
        SparePart.objects.filter(pk=self.old.pk).update(created_at=timezone.now() - datetime.timedelta(days=30))
        self.new = SparePart.objects.create(
            name='New', part_number='N-1', unit=self.unit, buying_price=1, selling_price=2,
        )

    def _filter(self, backend, query):
        request = APIRequestFactory().get('/', query)
        from rest_framework.request import Request
        return backend().filter_queryset(Request(request), SparePart.objects.all(), _View())

    def test_date_from_excludes_older_rows(self):
        cutoff = (timezone.localdate() - datetime.timedelta(days=7)).isoformat()
        names = set(self._filter(DateRangeFilter, {'date_from': cutoff}).values_list('name', flat=True))
        self.assertEqual(names, {'New'})

    def test_date_to_excludes_newer_rows(self):
        cutoff = (timezone.localdate() - datetime.timedelta(days=7)).isoformat()
        names = set(self._filter(DateRangeFilter, {'date_to': cutoff}).values_list('name', flat=True))
        self.assertEqual(names, {'Old'})

    def test_same_day_range_is_inclusive(self):
        today = timezone.localdate().isoformat()
        names = set(self._filter(DateRangeFilter, {'date_from': today, 'date_to': today}).values_list('name', flat=True))
        self.assertEqual(names, {'New'})

    def test_invalid_date_is_ignored(self):
        self.assertEqual(self._filter(DateRangeFilter, {'date_from': 'nope'}).count(), 2)

    def test_boolean_query_param_filter(self):
        names = set(self._filter(QueryParamFilter, {'is_active': 'false'}).values_list('name', flat=True))
        self.assertEqual(names, {'Old'})

    def test_category_has_created_at(self):
        self.assertIsNotNone(Category.objects.create(name='Brakes').created_at)


class ReportRangeTests(TestCase):
    def _request(self, **params):
        from rest_framework.request import Request
        return Request(APIRequestFactory().get('/', params))

    def test_range_over_a_year_is_rejected(self):
        from rest_framework.exceptions import ValidationError
        from reports.services import resolve_date_range
        with self.assertRaises(ValidationError):
            resolve_date_range(self._request(**{'from': '2024-01-01', 'to': '2025-06-01'}))

    def test_reversed_range_is_rejected(self):
        from rest_framework.exceptions import ValidationError
        from reports.services import resolve_date_range
        with self.assertRaises(ValidationError):
            resolve_date_range(self._request(**{'from': '2025-02-01', 'to': '2025-01-01'}))

    def test_long_range_buckets_by_month(self):
        from reports.services import resolve_date_range, resolve_granularity
        req = self._request(**{'from': '2025-01-01', 'to': '2025-12-31'})
        start, end = resolve_date_range(req)
        self.assertEqual(resolve_granularity(req, start, end), 'month')
