# Copyright 2021 - Today Coop IT Easy SC (<http://www.coopiteasy.be>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from datetime import date, datetime, timedelta

from odoo.exceptions import ValidationError

# absolute import is needed otherwise there is a recursive import
from odoo.addons.shift.tests.test_shift_common import TestShiftCommon


class TestShift(TestShiftCommon):
    def test_unsubscribe_worker_from_task_template_1(self):
        """
        Check that removing a worker from a task_template via the
        task_template remove the worker from the already generated
        shifts.
        """
        self._generate_shifts(days=1, nb=2)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 2)

        # Unsubscribe a worker from the task template
        self.task_template_1.worker_ids -= self.worker_regular_1

        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 1)

        # Subscribe a worker from the task template
        self.task_template_1.worker_ids += self.worker_regular_1

        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 2)

    def test_unsubscribe_worker_from_task_template_2(self):
        """
        Check that removing a worker from a task_template via the
        res_partner remove the worker from the already generated
        shifts.
        """
        self._generate_shifts(days=1, nb=2)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 2)

        # Unsubscribe a worker from the task template
        self.worker_regular_1.subscribed_shift_ids -= self.task_template_1

        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 1)

        # Subscribe a worker from the task template
        self.worker_regular_1.subscribed_shift_ids += self.task_template_1

        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 2)

    def test_subscribe_worker_from_task_template_with_shifts_1(self):
        """
        Check that adding a worker to a task_template via the
        task_template add the worker to already generated shifts.

        The task template has enough place. So there is empty generated
        shift where the new worker can be subscribed.
        """
        self._generate_shifts(days=1, nb=2)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_5), 2)

        self.task_template_1.worker_ids += self.worker_regular_5

        self.assertEqual(self._count_number_of_shift(self.worker_regular_5), 3)

    def test_subscribe_worker_from_task_template_without_shifts_1(self):
        """
        Check that adding a worker to a task_template via the
        task_template add the worker to already generated shifts.

        The task template is full. So there is no empty generated
        shift where the new worker can be subscribed.
        """
        self.task_template_1.worker_nb = 2
        self._generate_shifts(days=1, nb=2)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_5), 2)

        # Open a place for the new worker
        self.task_template_1.worker_nb = 3
        self.task_template_1.worker_ids += self.worker_regular_5

        self.assertEqual(self._count_number_of_shift(self.worker_regular_5), 3)

    def test_subscribe_supercooperator_to_shift_sets_them_as_shift_supercooperator(
        self,
    ):
        # if no super cooperator is set on template and worker is super cooperator
        # automatically set them as super cooperator.
        super_worker = self.worker_regular_1
        super_worker.user_ids = self.user_demo
        subscription_wizard = (
            self.env["shift.subscribe"]
            .with_context(active_id=super_worker.id)
            .create(
                {},
            )
        )
        shift = subscription_wizard.shift_id
        subscription_wizard.subscribe()
        self.assertEqual(shift.super_coop_id, super_worker.user_ids[0])

    def test_change_working_mode_1(self):
        """
        Check that changing a regular worker to irregular via the wizard
        removes the worker from the already generated shifts.
        """
        self._generate_shifts(days=1, nb=2)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 2)

        subscribe_wiz = self.subscribe_wizard.with_context(
            active_id=self.worker_regular_1.ids
        )
        subscribe_wiz = subscribe_wiz.create({"working_mode": "irregular"})
        subscribe_wiz.subscribe()

        self.assertTrue(self.worker_regular_1 not in self.task_template_1.worker_ids)
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 1)

    def test_change_worker_holiday_1(self):
        """
        Change the holiday start and end time on a cooperative_status.
        Check that worker is correctly unsbscribed or subscribed again
        to already generated shifts.
        """
        self._generate_shifts(days=7, nb=5)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        status_id = self.worker_regular_1.cooperative_status_ids
        status_id.today = date.today()

        # Set holiday in the past do not change shift for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "holiday_start_time": (status_id.today - timedelta(days=7)).isoformat(),
                "holiday_end_time": (status_id.today - timedelta(days=1)).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        # Set holiday in the future change shift for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "holiday_start_time": (status_id.today + timedelta(days=1)).isoformat(),
                "holiday_end_time": (status_id.today + timedelta(days=13)).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that beginning holiday later in the future change shift
        # for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "holiday_start_time": (
                    status_id.today + timedelta(days=14)
                ).isoformat(),
                "holiday_end_time": (status_id.today + timedelta(days=20)).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that shorten the holiday in the future change shift
        # for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "holiday_start_time": (status_id.today + timedelta(days=1)).isoformat(),
                "holiday_end_time": (status_id.today + timedelta(days=13)).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

    def test_change_worker_holiday_2(self):
        """
        Change the holiday start and end time on a cooperative_status.
        Check that worker is correctly unsbscribed or subscribed again
        to already generated shifts.

        This test uses the wizard.
        """
        self._generate_shifts(days=7, nb=5)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        holiday_wiz = self.holiday_wizard.with_context(
            active_id=self.worker_regular_1.ids
        )
        status_id = self.worker_regular_1.cooperative_status_ids
        status_id.today = date.today()

        # Set holiday in the past raise error and do not change shift
        # for worker_1
        holiday_wiz = holiday_wiz.create(
            {
                "holiday_start_day": (status_id.today - timedelta(days=7)).isoformat(),
                "holiday_end_day": (status_id.today - timedelta(days=1)).isoformat(),
            }
        )
        with self.assertRaises(ValidationError):
            holiday_wiz.holidays()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        # Set holiday in the future change shift for worker_1
        holiday_wiz = holiday_wiz.create(
            {
                "holiday_start_day": (status_id.today + timedelta(days=1)).isoformat(),
                "holiday_end_day": (status_id.today + timedelta(days=13)).isoformat(),
            }
        )
        holiday_wiz.holidays()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that beginning holiday later in the future change shift
        # for worker_1
        holiday_wiz = holiday_wiz.create(
            {
                "holiday_start_day": (status_id.today + timedelta(days=14)).isoformat(),
                "holiday_end_day": (status_id.today + timedelta(days=20)).isoformat(),
            }
        )
        holiday_wiz.holidays()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that shorten the holiday in the future change shift
        # for worker_1
        holiday_wiz = holiday_wiz.create(
            {
                "holiday_start_day": (status_id.today + timedelta(days=1)).isoformat(),
                "holiday_end_day": (status_id.today + timedelta(days=13)).isoformat(),
            }
        )
        holiday_wiz.holidays()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

    def test_change_worker_temporary_exemption_1(self):
        """
        Change the temporary exemption start and end time on a cooperative_status.
        Check that worker is correctly unsbscribed or subscribed again
        to already generated shifts.
        """
        self._generate_shifts(days=7, nb=5)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        status_id = self.worker_regular_1.cooperative_status_ids
        status_id.today = date.today()

        # Set exemption in the past do not change shift for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "temporary_exempt_start_date": (
                    date.today() - timedelta(days=7)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    date.today() - timedelta(days=1)
                ).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        # Set exemption in the future change shift for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "temporary_exempt_start_date": (
                    date.today() + timedelta(days=1)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    date.today() + timedelta(days=13)
                ).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that beginning exemption later in the future change shift
        # for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "temporary_exempt_start_date": (
                    date.today() + timedelta(days=14)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    date.today() + timedelta(days=20)
                ).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that shorten the exemption in the future change shift
        # for worker_1
        self.worker_regular_1.cooperative_status_ids.write(
            {
                "temporary_exempt_start_date": (
                    date.today() + timedelta(days=1)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    date.today() + timedelta(days=13)
                ).isoformat(),
            }
        )
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

    def test_change_worker_temporary_exemption_2(self):
        """
        Change the exemption start and end time on a cooperative_status.
        Check that worker is correctly unsbscribed or subscribed again
        to already generated shifts.

        This test uses the wizard.
        """
        self._generate_shifts(days=7, nb=5)

        # Check that initialisation works well
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        exemption_wiz = self.exemption_wizard.with_context(
            active_id=self.worker_regular_1.ids
        )
        status_id = self.worker_regular_1.cooperative_status_ids
        status_id.today = date.today()

        # Set holiday in the past raise error and do not change shift
        # for worker_1
        exemption_wiz = exemption_wiz.create(
            {
                "temporary_exempt_reason_id": self.exempt_reason_1.id,
                "temporary_exempt_start_date": (
                    status_id.today - timedelta(days=7)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    status_id.today - timedelta(days=1)
                ).isoformat(),
            }
        )
        with self.assertRaises(ValidationError):
            exemption_wiz.exempt()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 5)

        # Set holiday in the future change shift for worker_1
        exemption_wiz = exemption_wiz.create(
            {
                "temporary_exempt_reason_id": self.exempt_reason_1.id,
                "temporary_exempt_start_date": (
                    status_id.today + timedelta(days=1)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    status_id.today + timedelta(days=13)
                ).isoformat(),
            }
        )
        exemption_wiz.exempt()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that beginning holiday later in the future change shift
        # for worker_1
        exemption_wiz = exemption_wiz.create(
            {
                "temporary_exempt_reason_id": self.exempt_reason_1.id,
                "temporary_exempt_start_date": (
                    status_id.today + timedelta(days=14)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    status_id.today + timedelta(days=20)
                ).isoformat(),
            }
        )
        exemption_wiz.exempt()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

        # Check that shorten the holiday in the future change shift
        # for worker_1
        exemption_wiz = exemption_wiz.create(
            {
                "temporary_exempt_reason_id": self.exempt_reason_1.id,
                "temporary_exempt_start_date": (
                    status_id.today + timedelta(days=1)
                ).isoformat(),
                "temporary_exempt_end_date": (
                    status_id.today + timedelta(days=13)
                ).isoformat(),
            }
        )
        exemption_wiz.exempt()
        self.assertEqual(self._count_number_of_shift(self.worker_regular_1), 4)

    def test_irregular_worker_subscribed_to_shift_before_alert(self):
        self._generate_shifts(days=1, nb=2)
        self.assertFalse(self.worker_irregular_2.cooperative_status_ids.next_shift_id)

        some_empty_shift = self.shift_model.search(
            [("start_time", ">=", datetime.now()), ("worker_id", "=", False)],
            limit=1,
        )

        some_empty_shift.worker_id = self.worker_irregular_2
        self.assertEqual(
            self.worker_irregular_2.cooperative_status_ids.next_shift_id,
            some_empty_shift,
        )

        some_empty_shift.worker_id = False
        self.assertFalse(self.worker_irregular_2.cooperative_status_ids.next_shift_id)

    def test_unsubscribe_worker_from_task_template_computes_next_shift(self):
        self._generate_shifts(days=1, nb=2)
        datetime.now()
        # Check that initialisation works well
        next_shift = self.shift_model.search(
            [
                ("state", "=", "open"),
                ("worker_id", "=", self.worker_regular_1.id),
            ],
            order="start_time",
            limit=1,
        )
        self.assertEqual(
            self.worker_regular_1.cooperative_status_ids.next_shift_id,
            next_shift,
        )

        # Unsubscribe a worker from the task template
        self.task_template_1.worker_ids -= self.worker_regular_1
        next_shift = self.shift_model.search(
            [
                ("state", "=", "open"),
                ("worker_id", "=", self.worker_regular_1.id),
            ],
            order="start_time",
            limit=1,
        )
        self.assertEqual(
            self.worker_regular_1.cooperative_status_ids.next_shift_id,
            next_shift,
        )
        # Subscribe a worker from the task template
        self.task_template_1.worker_ids += self.worker_regular_1
        next_shift = self.shift_model.search(
            [
                ("state", "=", "open"),
                ("worker_id", "=", self.worker_regular_1.id),
            ],
            order="start_time",
            limit=1,
        )
        self.assertEqual(
            self.worker_regular_1.cooperative_status_ids.next_shift_id,
            next_shift,
        )
