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

from odoo import api, fields, models, _

import pytz


class HrLeave(models.Model):
    _inherit = "hr.leave"

    timesheet_ids = fields.One2many('account.analytic.line', 'holiday_id', string="Analytic Lines")

    def _validate_leave_request(self):
        self._generate_timesheets()
        return super()._validate_leave_request()

    def _generate_timesheets(self, ignored_resource_calendar_leaves=None):
        """ Timesheet will be generated on leave validation
            internal_project_id and leave_timesheet_task_id are used.
            The generated timesheet will be attached to this project/task.
        """
        vals_list = []
        leave_ids = []
        calendar_leaves_data = self.env['resource.calendar.leaves']._read_group([('holiday_id', 'in', self.ids)], ['holiday_id'], ['id:array_agg'])
        mapped_calendar_leaves = {leave: calendar_leave_ids[0] for leave, calendar_leave_ids in calendar_leaves_data}
        for leave in self:
            project, task = leave.employee_id.company_id.internal_project_id, leave.employee_id.company_id.leave_timesheet_task_id

            if not project or not task or leave.holiday_status_id.time_type == 'other':
                continue

            leave_ids.append(leave.id)
            if not leave.employee_id:
                continue

            calendar = leave.resource_calendar_id
            calendar_timezone = pytz.timezone((calendar or leave.employee_id).tz)

            if calendar.flexible_hours and leave.date_from.date() == leave.date_to.date():
                leave_date = leave.date_from.astimezone(calendar_timezone).date()
                if leave.request_unit_hours:
                    hours = leave.request_hour_to - leave.request_hour_from
                elif leave.request_unit_half and leave.request_date_from_period == leave.request_date_to_period:
                    hours = calendar.hours_per_day / 2
                else:  # Single-day leave
                    hours = calendar.hours_per_day
                work_hours_data = [(leave_date, hours)]
            else:
                ignored_resource_calendar_leaves = ignored_resource_calendar_leaves or []
                if leave in mapped_calendar_leaves:
                    ignored_resource_calendar_leaves.append(mapped_calendar_leaves[leave])
                work_hours_data = leave.employee_id._list_work_time_per_day(
                    leave.date_from,
                    leave.date_to,
                    domain=[('id', 'not in', ignored_resource_calendar_leaves)] if ignored_resource_calendar_leaves else None,
                    calendar=calendar,
                )[leave.employee_id.id]

            for index, (day_date, work_hours_count) in enumerate(work_hours_data):
                vals_list.append(leave._timesheet_prepare_line_values(index, work_hours_data, day_date, work_hours_count, project, task))

        # Unlink previous timesheets to avoid doublon (shouldn't happen on the interface but meh). Necessary when the function is called to regenerate timesheets.
        old_timesheets = self.env["account.analytic.line"].sudo().search([('project_id', '!=', False), ('holiday_id', 'in', leave_ids)])
        if old_timesheets:
            old_timesheets.holiday_id = False
            old_timesheets.unlink()

        self.env['account.analytic.line'].sudo().create(vals_list)

    def _timesheet_prepare_line_values(self, index, work_hours_data, day_date, work_hours_count, project, task):
        self.ensure_one()
        return {
            'name': _("Time Off (%(index)s/%(total)s)", index=index + 1, total=len(work_hours_data)),
            'project_id': project.id,
            'task_id': task.id,
            'account_id': project.sudo().account_id.id,
            'unit_amount': work_hours_count,
            'user_id': self.employee_id.user_id.id,
            'date': day_date,
            'holiday_id': self.id,
            'employee_id': self.employee_id.id,
            'company_id': task.sudo().company_id.id or project.sudo().company_id.id,
        }

    def _check_missing_global_leave_timesheets(self):
        if not self:
            return
        min_date = min(self.mapped('date_from'))
        max_date = max(self.mapped('date_to'))

        global_leaves = self.env['resource.calendar.leaves'].search([
            ("resource_id", "=", False),
            ("date_to", ">=", min_date),
            ("date_from", "<=", max_date),
            ("company_id.internal_project_id", "!=", False),
            ("company_id.leave_timesheet_task_id", "!=", False),
        ])
        if global_leaves:
            global_leaves._generate_public_time_off_timesheets(self.employee_id)

    def action_refuse(self):
        """ Remove the timesheets linked to the refused holidays """
        result = super().action_refuse()
        timesheets = self.sudo().mapped('timesheet_ids')
        timesheets.write({'holiday_id': False})
        timesheets.unlink()
        self._check_missing_global_leave_timesheets()
        return result

    def _action_user_cancel(self, reason=None):
        res = super()._action_user_cancel(reason)
        timesheets = self.sudo().timesheet_ids
        timesheets.write({'holiday_id': False})
        timesheets.unlink()
        self._check_missing_global_leave_timesheets()
        return res

    def _force_cancel(self, *args, **kwargs):
        super()._force_cancel(*args, **kwargs)
        # override this method to reevaluate timesheets after the leaves are updated via force cancel
        timesheets = self.sudo().timesheet_ids
        timesheets.holiday_id = False
        timesheets.unlink()

    def write(self, vals):
        res = super().write(vals)
        # reevaluate timesheets after the leaves are wrote in order to remove empty timesheets
        timesheet_ids_to_remove = []
        for leave in self:
            if leave.number_of_days == 0 and leave.sudo().timesheet_ids:
                timesheet_ids_to_remove.extend(leave.timesheet_ids.ids)
                leave.sudo().timesheet_ids.holiday_id = False
        self.env['account.analytic.line'].browse(set(timesheet_ids_to_remove)).sudo().unlink()
        return res

    @api.ondelete(at_uninstall=False)
    def _unlink_timesheets(self):
        """ Remove timesheets when the timeoff is deleted. """
        timesheets = self.sudo().timesheet_ids
        timesheets.write({'holiday_id': False})
        timesheets.unlink()
        self._check_missing_global_leave_timesheets()
