# -*- coding: utf-8 -*-
"""STEP 4 tests — deadline engine.

Run just this step:
    odoo -d <db> -u activeblue_familylaw --test-enable \
         --test-tags familylaw_step4 --stop-after-init

Date math is tested with FIXED dates (never today()) for determinism. Overdue
behaviour is tested with dates safely in the past/future relative to any run date.
"""

from datetime import date

from odoo.tests.common import TransactionCase, tagged


@tagged("post_install", "-at_install", "familylaw", "familylaw_step4")
class TestStep4DeadlineEngine(TransactionCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.partner = cls.env["res.partner"].create({"name": "Deadline Client"})
        cls.case = cls.env["familylaw.case"].create({
            "name": "Deadline Matter",
            "client_id": cls.partner.id,
            "case_type": "support_modification",
        })
        cls.proc = cls.case.proceeding_ids[0]
        cls.Deadline = cls.env["familylaw.deadline"]

    def _mk(self, **kw):
        vals = {
            "proceeding_id": self.proc.id,
            "name": "Test Deadline",
            "deadline_type": "custom",
            "trigger_date": date(2025, 1, 1),
            "days": 20,
        }
        vals.update(kw)
        return self.Deadline.create(vals)

    # --- basic math (exclude trigger day, add N days) -----------------------
    def test_01_twenty_day_math(self):
        # 2025-01-01 (Wed) + 20 days = 2025-01-21 (Tue), no weekend roll needed
        dl = self._mk(trigger_date=date(2025, 1, 1), days=20)
        self.assertEqual(dl.due_date, date(2025, 1, 21))

    def test_02_forty_five_day_math(self):
        # 2025-01-01 + 45 = 2025-02-15 (Sat) -> roll to 2025-02-17 (Mon)
        dl = self._mk(trigger_date=date(2025, 1, 1), days=45)
        self.assertEqual(dl.due_date, date(2025, 2, 17))

    def test_03_thirty_day_math(self):
        # 2025-01-01 + 30 = 2025-01-31 (Fri), no roll
        dl = self._mk(trigger_date=date(2025, 1, 1), days=30)
        self.assertEqual(dl.due_date, date(2025, 1, 31))

    # --- weekend roll-forward -----------------------------------------------
    def test_04_saturday_rolls_to_monday(self):
        # 2025-03-07 (Fri) + 1 = 2025-03-08 (Sat) -> 2025-03-10 (Mon)
        dl = self._mk(trigger_date=date(2025, 3, 7), days=1)
        self.assertEqual(dl.due_date, date(2025, 3, 10))

    def test_05_sunday_rolls_to_monday(self):
        # 2025-03-07 (Fri) + 2 = 2025-03-09 (Sun) -> 2025-03-10 (Mon)
        dl = self._mk(trigger_date=date(2025, 3, 7), days=2)
        self.assertEqual(dl.due_date, date(2025, 3, 10))

    def test_06_weekday_no_roll(self):
        # 2025-03-07 (Fri) + 4 = 2025-03-11 (Tue), no roll
        dl = self._mk(trigger_date=date(2025, 3, 7), days=4)
        self.assertEqual(dl.due_date, date(2025, 3, 11))

    # --- standard offsets default from type ---------------------------------
    def test_07_answer_default_days(self):
        dl = self.Deadline.create({
            "proceeding_id": self.proc.id,
            "name": "Answer due",
            "deadline_type": "answer",
            "trigger_date": date(2025, 1, 1),
        })
        self.assertEqual(dl.days, 20)

    def test_08_disclosure_default_days(self):
        dl = self.Deadline.create({
            "proceeding_id": self.proc.id,
            "name": "Disclosure due",
            "deadline_type": "disclosure",
            "trigger_date": date(2025, 1, 1),
        })
        self.assertEqual(dl.days, 45)

    def test_09_explicit_days_overrides_default(self):
        dl = self.Deadline.create({
            "proceeding_id": self.proc.id,
            "name": "Custom answer",
            "deadline_type": "answer",
            "trigger_date": date(2025, 1, 1),
            "days": 5,
        })
        self.assertEqual(dl.days, 5)

    # --- overdue detection --------------------------------------------------
    def test_10_past_deadline_is_overdue(self):
        dl = self._mk(trigger_date=date(2000, 1, 1), days=20)
        self.assertTrue(dl.is_overdue)

    def test_11_future_deadline_not_overdue(self):
        dl = self._mk(trigger_date=date(2999, 1, 1), days=20)
        self.assertFalse(dl.is_overdue)

    def test_12_done_deadline_not_overdue(self):
        dl = self._mk(trigger_date=date(2000, 1, 1), days=20)
        dl.action_mark_done()
        self.assertFalse(dl.is_overdue)
        self.assertEqual(dl.state, "done")

    def test_13_cron_flags_overdue(self):
        dl = self._mk(trigger_date=date(2000, 1, 1), days=20)
        self.assertEqual(dl.state, "pending")
        self.Deadline._cron_flag_overdue()
        self.assertEqual(dl.state, "overdue")

    def test_14_cron_ignores_future(self):
        dl = self._mk(trigger_date=date(2999, 1, 1), days=20)
        self.Deadline._cron_flag_overdue()
        self.assertEqual(dl.state, "pending")

    def test_15_is_overdue_search(self):
        past = self._mk(trigger_date=date(2000, 1, 1), days=20)
        self._mk(trigger_date=date(2999, 1, 1), days=20)
        found = self.Deadline.search([
            ("is_overdue", "=", True),
            ("proceeding_id", "=", self.proc.id),
        ])
        self.assertIn(past, found)

    # --- calendar mirror ----------------------------------------------------
    def test_16_calendar_event_created(self):
        dl = self._mk()
        self.assertTrue(dl.calendar_event_id)
        self.assertEqual(dl.calendar_event_id.start_date, dl.due_date)

    def test_17_calendar_event_updates_on_date_change(self):
        dl = self._mk(trigger_date=date(2025, 1, 1), days=20)
        first = dl.calendar_event_id.start_date
        dl.days = 30
        self.assertNotEqual(dl.calendar_event_id.start_date, first)
        self.assertEqual(dl.calendar_event_id.start_date, dl.due_date)

    # --- per-proceeding isolation -------------------------------------------
    def test_18_deadlines_isolated_per_proceeding(self):
        proc2 = self.env["familylaw.proceeding"].create({
            "case_id": self.case.id,
            "name": "Second Proceeding",
            "proceeding_type": "modification",
        })
        d1 = self._mk()
        d2 = self.Deadline.create({
            "proceeding_id": proc2.id,
            "name": "Other deadline",
            "deadline_type": "custom",
            "trigger_date": date(2025, 1, 1),
            "days": 10,
        })
        self.assertIn(d1, self.proc.deadline_ids)
        self.assertNotIn(d2, self.proc.deadline_ids)
        self.assertIn(d2, proc2.deadline_ids)

    # --- seeding ------------------------------------------------------------
    def test_19_seed_standard_deadlines(self):
        self.proc.action_seed_standard_deadlines()
        types = set(self.proc.deadline_ids.mapped("deadline_type"))
        self.assertEqual(types, {"answer", "disclosure", "discovery"})

    def test_20_seed_is_idempotent(self):
        self.proc.action_seed_standard_deadlines()
        n1 = len(self.proc.deadline_ids)
        self.proc.action_seed_standard_deadlines()
        self.assertEqual(len(self.proc.deadline_ids), n1)
