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

from datetime import date

from odoo import api, fields, models
from odoo.fields import Domain

from odoo.addons.resource.models.utils import HOURS_PER_DAY
from odoo.tools.float_utils import float_round


class HrLeaveAllocationGenerateMultiWizard(models.TransientModel):
    _name = 'hr.leave.allocation.generate.multi.wizard'
    _inherit = ['hr.mixin']
    _description = 'Generate time off allocations for multiple employees'

    def _get_employee_domain(self):
        domain = (
            Domain([("company_id", "=", self.company_id.id)])
            if self.company_id
            else Domain([("company_id", "in", self.env.companies.ids)])
        )
        if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
            domain &= Domain(['|', ('leave_manager_id', '=', self.env.user.id), ('user_id', '=', self.env.user.id)])
        return domain

    def _domain_work_entry_type_id(self):
        domain = [
            ('requires_allocation', '=', True),
        ]
        if self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
            return domain
        return Domain.AND([domain, [('employee_requests', '=', True)]])

    name = fields.Char("Description", compute="_compute_name", store=True, readonly=False)
    duration = fields.Float(string="Allocation")
    work_entry_type_id = fields.Many2one(
        "hr.work.entry.type", string="Time Off Type", required=True,
        domain=_domain_work_entry_type_id)
    allowed_work_entry_type_ids = fields.Many2many(
        'hr.work.entry.type', compute='_compute_allowed_work_entry_type_ids')
    unit_of_measure = fields.Selection(related="work_entry_type_id.unit_of_measure")
    employee_ids = fields.Many2many('hr.employee', string='Employees', domain=lambda self: self._get_employee_domain())
    company_id = fields.Many2one('res.company', default=lambda self: self.env.company, required=True)
    allocation_type = fields.Selection([
        ('regular', 'Regular Allocation'),
        ('accrual', 'Based on Accrual Plan')
    ], string="Allocation Type", default="regular", required=True)
    accrual_plan_id = fields.Many2one('hr.leave.accrual.plan')
    date_from = fields.Date('Start Date', default=fields.Date.context_today, required=True)
    date_to = fields.Date('End Date')
    notes = fields.Text('Reasons')

    @api.depends('company_id')
    def _compute_allowed_work_entry_type_ids(self):
        for wizard in self:
            country = wizard.company_id.country_id or self.env.company.country_id
            if not country or not self.env['hr.work.entry.type'].search_count([('country_id', '=', country.id)], limit=1):
                domain = [('country_id', '=', False)]
            else:
                domain = [('country_id', '=', country.id)]
            domain = Domain.AND([wizard._domain_work_entry_type_id(), domain])
            wizard.allowed_work_entry_type_ids = self.env['hr.work.entry.type'].search(domain)

    @api.depends('work_entry_type_id', 'duration')
    def _compute_name(self):
        for allocation_multi in self:
            allocation_multi.name = allocation_multi._get_title()

    def _get_title(self):
        self.ensure_one()
        if not self.work_entry_type_id:
            return self.env._("Allocation Request")
        return self.env._(
            '%(name)s (%(duration)s %(unit_of_measure)s(s))',
            name=self.work_entry_type_id.name,
            duration=float_round(self.duration, precision_digits=2),
            unit_of_measure=self.unit_of_measure
        )

    def _prepare_allocation_values(self, employees):
        self.ensure_one()
        hours_per_day = {
            e.id: e.resource_calendar_id.hours_per_day or self.company_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY
            for e in employees.sudo()
        }
        return [{
            'name': self.name,
            'work_entry_type_id': self.work_entry_type_id.id,
            'number_of_days': self.duration if self.unit_of_measure != "hour" else self.duration / hours_per_day[employee.id],
            'employee_id': employee.id,
            'state': 'confirm',
            'allocation_type': self.allocation_type,
            'date_from': self.date_from,
            'date_to': self.date_to,
            'accrual_plan_id': self.accrual_plan_id.id,
            'notes': self.notes
        } for employee in employees]

    def action_generate_allocations(self):
        self.ensure_one()
        employees = self.employee_ids or self.env['hr.employee'].search(self._get_employee_domain())
        vals_list = self._prepare_allocation_values(employees)
        if vals_list:
            allocations = self.env['hr.leave.allocation'].with_context(
                mail_notify_force_send=False,
                mail_activity_automation_skip=True,
            ).create(vals_list)
            accrual_allocations = allocations.filtered(lambda a: a.allocation_type == 'accrual')
            if not self.duration:
                for date_to, allocation in accrual_allocations.grouped('date_to').items():
                    date_to = min(date_to, date.today()) if date_to else False
                    update_vals = allocation._get_initialize_accrual_plan_values(self.date_from)
                    allocation.update(update_vals)
                    allocation._process_accrual_plans(date_to)
            allocations.filtered(lambda c: c.validation_type not in ('no_validation', 'hr')).action_approve()
            if self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
                allocations.filtered(lambda c: c.validation_type == 'hr').action_approve()

            return {
                'type': 'ir.actions.act_window',
                'name': self.env._('Generated Allocations'),
                "views": [[self.env.ref('hr_holidays.hr_leave_allocation_view_tree').id, "list"], [self.env.ref('hr_holidays.hr_leave_allocation_view_form_manager').id, "form"]],
                'view_mode': 'list',
                'res_model': 'hr.leave.allocation',
                'domain': [('id', 'in', allocations.ids)],
                'context': {
                    'active_id': False,
                },
            }
        return None
