# Part of Odoo. See LICENSE file for full copyright and licensing details.

from datetime import datetime

from odoo import Command, fields
from odoo.tests import common, Form
from odoo.tests.common import TransactionCase
from odoo.fields import Datetime
from odoo.addons.hr.tests.test_utils import get_admin_employee
from odoo.addons.mail.tests.common import mail_new_test_user


class TestHrHolidaysCommon(common.TransactionCase):

    @classmethod
    def setUpClass(cls):
        super(TestHrHolidaysCommon, cls).setUpClass()
        cls.env.user.tz = 'Europe/Brussels'
        cls.env.user.company_id.tz = "Europe/Brussels"

        cls.company = cls.env['res.company'].create({'name': 'Test company'})
        cls.external_company = cls.env['res.company'].create({'name': 'External Test company'})

        cls.company.resource_calendar_id = cls.env['resource.calendar'].create({
            'attendance_ids': [
                (0, 0,
                    {
                        'dayofweek': weekday,
                        'hour_from': hour,
                        'hour_to': hour + 4,
                    })
                for weekday in ['0', '1', '2', '3', '4']
                for hour in [8, 13]
            ],
            'company_id': cls.company.id,
            'name': 'Standard 40h/week',
        })

        cls.external_company.resource_calendar_id = cls.env['resource.calendar'].create({
            'attendance_ids': [
                (0, 0,
                    {
                        'dayofweek': weekday,
                        'hour_from': hour,
                        'hour_to': hour + 4,
                    })
                for weekday in ['0', '1', '2', '3', '4']
                for hour in [8, 13]
            ],
            'company_id': cls.external_company.id,
            'name': 'Standard 40h/week',
        })

        cls.env.user.company_id = cls.company

        # The available time off types are the ones whose:
        # 1. Company is one of the selected companies.
        # 2. Company is false but whose country is one the countries of the selected companies.
        # 3. Company is false and country is false
        # Thus, a time off type is defined to be available for `Test company`
        # For example, the tour 'time_off_request_calendar_view' would succeed (false positive) without this leave type.
        # However, the tour won't create a time-off request (as expected)because no time-off type is available to be selected on the leave
        # This would cause the test case that uses the tour to fail.
        cls.env['hr.work.entry.type'].create({
            'name': 'Test Work Entry Type',
            'code': 'Test Work Entry Type',
            'requires_allocation': False,
            'request_unit': 'day',
            'unit_of_measure': 'day',
        })

        cls.admin_employee = get_admin_employee(cls.env)

        # Test users to use through the various tests
        cls.user_hruser = mail_new_test_user(cls.env, login='armande', groups='base.group_user,hr_holidays.group_hr_holidays_user')
        cls.user_hruser_id = cls.user_hruser.id

        cls.user_hrmanager = mail_new_test_user(cls.env, login='bastien', groups='base.group_user,hr_holidays.group_hr_holidays_manager')
        cls.user_hrmanager_id = cls.user_hrmanager.id
        cls.user_hrmanager.tz = 'Europe/Brussels'

        cls.user_responsible = mail_new_test_user(cls.env, login='Titus', groups='base.group_user,hr_holidays.group_hr_holidays_responsible')
        cls.user_responsible_id = cls.user_responsible.id
        cls.user_employee = mail_new_test_user(cls.env, login='enguerran', password='enguerran', groups='hr_holidays.group_hr_holidays_employee')
        cls.user_employee_id = cls.user_employee.id
        cls.external_user_employee = mail_new_test_user(cls.env, login='external', password='external', groups='base.group_user')
        cls.external_user_employee_id = cls.external_user_employee.id
        # Hr Data
        Department = cls.env['hr.department'].with_context(tracking_disable=True)

        cls.hr_dept = Department.create({
            'name': 'Human Resources',
        })
        cls.rd_dept = Department.create({
            'name': 'Research and devlopment',
        })

        cls.employee_responsible = cls.env['hr.employee'].create({
            'name': 'David Employee',
            'user_id': cls.user_responsible_id,
            'department_id': cls.rd_dept.id,
        })

        cls.employee_emp = cls.env['hr.employee'].create({
            'name': 'David Employee',
            'user_id': cls.user_employee_id,
            'leave_manager_id': cls.user_responsible_id,
            'department_id': cls.rd_dept.id,
            'company_id': cls.company.id,
        })
        cls.employee_emp_id = cls.employee_emp.id

        cls.employee_external = cls.env['hr.employee'].create({
            'name': 'external Employee',
            'user_id': cls.external_user_employee_id,
            'company_id': cls.external_company.id,
        })
        cls.external_employee_id = cls.employee_external.id

        cls.employee_hruser = cls.env['hr.employee'].create({
            'name': 'Armande HrUser',
            'user_id': cls.user_hruser_id,
            'department_id': cls.rd_dept.id,
        })
        cls.employee_hruser_id = cls.employee_hruser.id

        cls.employee_hrmanager = cls.env['hr.employee'].create({
            'name': 'Bastien HrManager',
            'user_id': cls.user_hrmanager_id,
            'department_id': cls.hr_dept.id,
            'parent_id': cls.employee_hruser_id,
        })
        cls.employee_hrmanager_id = cls.employee_hrmanager.id

        cls.rd_dept.write({'manager_id': cls.employee_hruser_id})
        cls.hours_per_day = cls.employee_emp.resource_id.calendar_id.hours_per_day or 8

    def assert_remaining_leaves_equal(self, work_entry_type, value, employee, date=None, digits=None):
        allocation_data = work_entry_type.get_allocation_data(employee, date)
        if not date:
            date = fields.Date.today()
        if digits:
            self.assertAlmostEqual(allocation_data[employee][0][1]['remaining_leaves'], value,
                digits, f"Remaining leaves for date '{date}' are incorrect.")
        else:
            self.assertEqual(allocation_data[employee][0][1]['remaining_leaves'],
                value, f"Remaining leaves for date '{date}' are incorrect.")

    def _take_leave(self, employee, work_entry_type, date_from, date_to):
        leave = self.env['hr.leave'].create({
            'name': 'Leave',
            'employee_id': employee.id,
            'work_entry_type_id': work_entry_type.id,
            'request_date_from': date_from,
            'request_date_to': date_to,
        })
        return leave

    def _create_form_test_accrual_allocation(self, work_entry_type, date_from, employee, accrual_plan, date_to=None, creator_user=None):
        allocation = self.env['hr.leave.allocation']
        if creator_user:
            allocation = allocation.with_user(creator_user)
        with Form(allocation, 'hr_holidays.hr_leave_allocation_view_form_manager') as form:
            form.name = 'Test accrual allocation'
            form.allocation_type = 'accrual'
            form.accrual_plan_id = accrual_plan
            form.employee_id = employee
            form.work_entry_type_id = work_entry_type
            form.date_from = date_from
            if date_to:
                form.date_to = date_to
        return form.record

    def _create_form_test_regular_allocation(self, work_entry_type, date_from, employee, number_of_days, date_to=None, creator_user=None):
        allocation = self.env['hr.leave.allocation']
        if creator_user:
            allocation = allocation.with_user(creator_user)
        with Form(allocation, 'hr_holidays.hr_leave_allocation_view_form_manager') as form:
            form.name = 'Test regular allocation'
            form.allocation_type = 'regular'
            form.employee_id = employee
            form.work_entry_type_id = work_entry_type
            form.date_from = date_from
            form.number_of_days_display = number_of_days
            if date_to:
                form.date_to = date_to
        return form.record


class TestHolidayContract(TransactionCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        cls.env.company.resource_calendar_id = cls.env['resource.calendar'].create({
            'attendance_ids': [
                (0, 0,
                    {
                        'dayofweek': weekday,
                        'hour_from': hour,
                        'hour_to': hour + 4,
                    })
                for weekday in ['0', '1', '2', '3', '4']
                for hour in [8, 13]
            ],
            'name': 'Standard 40h/week',
        })

        cls.work_entry_type = cls.env['hr.work.entry.type'].create({
            'name': 'Legal Leaves',
            'code': 'Legal Leaves',
            'count_as': 'absence',
            'requires_allocation': False,
            'request_unit': 'day',
            'unit_of_measure': 'day',
        })
        cls.env.ref('base.user_admin').notification_type = 'inbox'

        cls.dep_rd = cls.env['hr.department'].create({
            'name': 'Research & Development - Test',
        })

        # I create a new employee "Jules"
        cls.jules_emp = cls.env['hr.employee'].create({
            'name': 'Jules',
            'sex': 'male',
            'birthday': '1984-05-01',
            'country_id': cls.env.ref('base.be').id,
            'department_id': cls.dep_rd.id,
        })

        cls.calendar_35h = cls.env['resource.calendar'].create({
            'name': '35h calendar',
            'attendance_ids': [
                (0, 0, {'dayofweek': '0', 'hour_from': 8, 'hour_to': 12}),
                (0, 0, {'dayofweek': '0', 'hour_from': 13, 'hour_to': 16}),
                (0, 0, {'dayofweek': '1', 'hour_from': 8, 'hour_to': 12}),
                (0, 0, {'dayofweek': '1', 'hour_from': 13, 'hour_to': 16}),
                (0, 0, {'dayofweek': '2', 'hour_from': 8, 'hour_to': 12}),
                (0, 0, {'dayofweek': '2', 'hour_from': 13, 'hour_to': 16}),
                (0, 0, {'dayofweek': '3', 'hour_from': 8, 'hour_to': 12}),
                (0, 0, {'dayofweek': '3', 'hour_from': 13, 'hour_to': 16}),
                (0, 0, {'dayofweek': '4', 'hour_from': 8, 'hour_to': 12}),
                (0, 0, {'dayofweek': '4', 'hour_from': 13, 'hour_to': 16})
            ]
        })
        cls.calendar_40h = cls.env['resource.calendar'].create({'name': 'Default calendar'})

        # This contract ends at the 15th of the month
        cls.jules_emp.version_id.write({  # Fixed term contract
            'contract_date_end': datetime.strptime('2015-11-15', '%Y-%m-%d'),
            'contract_date_start': datetime.strptime('2015-01-01', '%Y-%m-%d'),
            'date_version': datetime.strptime('2015-01-01', '%Y-%m-%d'),
            'name': 'First CDD Contract for Jules',
            'resource_calendar_id': cls.calendar_40h.id,
            'wage': 5000.0,
        })
        cls.contract_cdd = cls.jules_emp.version_id

        # This contract starts the next day
        cls.contract_cdi = cls.jules_emp.create_version({
            'date_version': datetime.strptime('2015-11-16', '%Y-%m-%d'),
            'contract_date_start': datetime.strptime('2015-11-16', '%Y-%m-%d'),
            'contract_date_end': False,
            'name': 'Contract for Jules',
            'resource_calendar_id': cls.calendar_35h.id,
            'wage': 5000.0,
        })

    @classmethod
    def create_leave(cls, date_from=None, date_to=None, name="", employee_id=False):
        return cls.env['hr.leave'].create({
            'name': name or 'Holiday!!!',
            'employee_id': employee_id or cls.richard_emp.id,
            'work_entry_type_id': cls.work_entry_type.id,
            'request_date_to': date_to or Datetime.today(),
            'request_date_from': date_from or Datetime.today(),
        })
