# Copyright 2025 Criptomart
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

"""
Test suite for edge cases involving dates, times, and calendar calculations.

Coverage:
- Leap year (Feb 29) handling
- Long-duration orders (entire year)
- Pickup day boundary conditions
- Orders with future start dates
- Orders without end dates
- Extreme dates (year 1900, year 2099)
"""

from datetime import date
from datetime import timedelta

from dateutil.relativedelta import relativedelta

from odoo.exceptions import ValidationError  # noqa: F401
from odoo.tests.common import TransactionCase


class TestLeapYearHandling(TransactionCase):
    """Test date calculations with leap year (Feb 29)."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_order_spans_leap_day(self):
        """Test order that includes Feb 29 (leap year)."""
        # 2024 is a leap year
        start = date(2024, 2, 25)
        end = date(2024, 3, 3)  # Spans Feb 29

        order = self.env["group.order"].create(
            {
                "name": "Leap Year Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "2",  # Wednesday (Feb 28 or 29 depending on week)
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Should correctly calculate pickup date
        self.assertTrue(order.pickup_date)

    def test_pickup_day_on_feb_29(self):
        """Test setting pickup_day to land on Feb 29."""
        # 2024 Feb 29 is a Thursday (day 3)
        start = date(2024, 2, 26)  # Monday
        end = date(2024, 3, 3)

        order = self.env["group.order"].create(
            {
                "name": "Feb 29 Pickup",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",  # Thursday = Feb 29
                "cutoff_day": "0",
            }
        )

        self.assertEqual(order.pickup_date, date(2024, 2, 29))

    def test_order_before_leap_day(self):
        """Test order in non-leap year (no Feb 29)."""
        # 2023 is NOT a leap year
        start = date(2023, 2, 25)
        end = date(2023, 3, 3)

        order = self.env["group.order"].create(
            {
                "name": "Non-Leap Year Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "2",
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Pickup should be Feb 28 (last day of Feb)
        self.assertIn(order.pickup_date.month, [2, 3])


class TestLongDurationOrders(TransactionCase):
    """Test orders spanning very long periods."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_order_spans_entire_year(self):
        """Test order running for 365 days."""
        start = date(2024, 1, 1)
        end = date(2024, 12, 31)

        order = self.env["group.order"].create(
            {
                "name": "Year-Long Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",  # Same day each week
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Should handle 52+ weeks correctly
        days_diff = (end - start).days
        self.assertEqual(days_diff, 365)

    def test_order_multiple_years(self):
        """Test order spanning multiple years (2+ years)."""
        start = date(2024, 1, 1)
        end = date(2026, 12, 31)  # 3 years

        order = self.env["group.order"].create(
            {
                "name": "Multi-Year Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "monthly",
                "pickup_day": "15",
                "cutoff_day": "10",
            }
        )

        self.assertTrue(order.exists())
        days_diff = (end - start).days
        self.assertGreater(days_diff, 700)  # More than 2 years

    def test_order_one_day_duration(self):
        """Test order with start_date == end_date (single day)."""
        same_day = date(2024, 2, 15)

        order = self.env["group.order"].create(
            {
                "name": "One-Day Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "once",
                "start_date": same_day,
                "end_date": same_day,
                "period": "once",
                "pickup_day": "0",
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())


class TestPickupDayBoundary(TransactionCase):
    """Test pickup_day calculations at boundaries."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_pickup_day_same_as_start_date(self):
        """Test when pickup_day equals start date (today)."""
        today = date.today()
        start = today
        end = today + timedelta(days=7)

        order = self.env["group.order"].create(
            {
                "name": "Today Pickup",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": str(start.weekday()),  # Same as start
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Pickup should be today
        self.assertEqual(order.pickup_date, start)

    def test_pickup_day_last_day_of_month(self):
        """Test pickup day on last day of month (Jan 31, Feb 28/29, etc)."""
        # Start on Jan 24, pickup on Jan 31
        start = date(2024, 1, 24)
        end = date(2024, 2, 1)

        order = self.env["group.order"].create(
            {
                "name": "Month-End Pickup",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "once",
                "pickup_day": "2",  # Wednesday = Jan 31
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())

    def test_pickup_day_month_boundary(self):
        """Test when pickup crosses month boundary."""
        # Start Jan 28, pickup might be in February
        start = date(2024, 1, 28)
        end = date(2024, 2, 5)

        order = self.env["group.order"].create(
            {
                "name": "Month Boundary Pickup",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "4",  # Friday (Feb 2)
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Pickup should be in Feb
        self.assertEqual(order.pickup_date.month, 2)

    def test_all_seven_days_as_pickup(self):
        """Test each day of week (0-6) as valid pickup_day."""
        start = date(2024, 1, 1)  # Monday
        end = date(2024, 1, 8)

        for day_num in range(7):
            order = self.env["group.order"].create(
                {
                    "name": f"Pickup Day {day_num}",
                    "group_ids": [(6, 0, [self.group.id])],
                    "type": "regular",
                    "start_date": start,
                    "end_date": end,
                    "period": "weekly",
                    "pickup_day": str(day_num),
                    "cutoff_day": "0",
                }
            )

            self.assertTrue(order.exists())
            # Each should have valid pickup_date
            self.assertTrue(order.pickup_date)


class TestFutureStartDateOrders(TransactionCase):
    """Test orders that start in the future."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_order_starts_tomorrow(self):
        """Test order starting tomorrow."""
        today = date.today()
        start = today + timedelta(days=1)
        end = start + timedelta(days=7)

        order = self.env["group.order"].create(
            {
                "name": "Future Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        self.assertGreater(order.start_date, today)

    def test_order_starts_6_months_future(self):
        """Test order starting 6 months from now."""
        today = date.today()
        start = today + relativedelta(months=6)
        end = start + timedelta(days=30)

        order = self.env["group.order"].create(
            {
                "name": "Far Future Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "monthly",
                "pickup_day": "15",
                "cutoff_day": "10",
            }
        )

        self.assertTrue(order.exists())


class TestExtremeDate(TransactionCase):
    """Test edge cases with very old or very new dates."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_order_year_2000(self):
        """Test order in year 2000 (Y2K edge case)."""
        start = date(2000, 1, 1)
        end = date(2000, 12, 31)

        order = self.env["group.order"].create(
            {
                "name": "Y2K Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())

    def test_order_far_future_2099(self):
        """Test order in far future (year 2099)."""
        start = date(2099, 1, 1)
        end = date(2099, 12, 31)

        order = self.env["group.order"].create(
            {
                "name": "Far Future Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())

    def test_order_crossing_century(self):
        """Test order spanning century boundary (Dec 1999 to Jan 2000)."""
        start = date(1999, 12, 26)
        end = date(2000, 1, 2)

        order = self.env["group.order"].create(
            {
                "name": "Century Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "6",  # Saturday
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # Should handle date arithmetic correctly across years
        self.assertEqual(order.start_date.year, 1999)
        self.assertEqual(order.end_date.year, 2000)


class TestOrderWithoutEndDate(TransactionCase):
    """Test orders without explicit end_date (permanent/ongoing)."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_permanent_order_with_null_end_date(self):
        """Test order with end_date = NULL (ongoing order)."""
        start = date.today()

        self.env["group.order"].create(
            {
                "name": "Permanent Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": False,  # No end date
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        # If supported, should handle gracefully
        # Otherwise, may be optional validation


class TestPickupCalculationAccuracy(TransactionCase):
    """Test accuracy of pickup_date calculations."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

    def test_pickup_date_calculation_multiple_weeks(self):
        """Test pickup_date calculation over multiple weeks."""
        # Week 1: Jan 1-7 (Mon-Sun), pickup Thursday = Jan 4
        start = date(2024, 1, 1)
        end = date(2024, 1, 22)

        order = self.env["group.order"].create(
            {
                "name": "Multi-Week Pickup",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "weekly",
                "pickup_day": "3",  # Thursday
                "cutoff_day": "0",
            }
        )

        self.assertTrue(order.exists())
        # First pickup should be first Thursday on or after start
        self.assertEqual(order.pickup_date.weekday(), 3)

    def test_monthly_order_pickup_date(self):
        """Test pickup_date for monthly orders."""
        # Order runs Feb 1 - Mar 31, pickup on 15th
        start = date(2024, 2, 1)
        end = date(2024, 3, 31)

        order = self.env["group.order"].create(
            {
                "name": "Monthly Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start,
                "end_date": end,
                "period": "monthly",
                "pickup_day": "15",
                "cutoff_day": "10",
            }
        )

        self.assertTrue(order.exists())
        # First pickup should be Feb 15
        self.assertGreaterEqual(order.pickup_date.day, 15)
