# -*- coding: utf-8 -*-

from odoo import api, fields, models, _, exceptions, Command


class Appraisal(models.Model):
    _name = 'hr.employee.appraisal'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _rec_name = 'employee_id'
    _description = 'Appraisal'

    date_from = fields.Date()
    date_to = fields.Date()
    great_level = fields.Float()
    level_achieved = fields.Float()
    level_achieved_percentage = fields.Float(tracking=True)
    appraisal_date = fields.Date()
    is_manager = fields.Boolean(related='appraisal_plan_id.is_manager')
    state = fields.Selection([
        ("draft", "Draft"), 
        ('employee_confirmation', 'Employee Confirmation'),
        ('employee_rejection', 'Rejected by the Employee'),
        ("state_done", "Done"), 
        ("closed", "Closed")
    ], default='draft', tracking=True)
    start_compute = fields.Char(
        compute='fill_employee_or_manager_appraisal')  # Invisible filed to call compute function

    # Relational fields
    employee_appraisal = fields.Many2one('hr.group.employee.appraisal')  # Inverse field
    employee_id = fields.Many2one('hr.employee')
    appraisal_plan_id = fields.Many2one('appraisal.plan')  # to store the main appraisal
    # appraisal_plan_complete_id = fields.Many2one('appraisal.plan.complete')
    standard_appraisal_employee_line_ids = fields.One2many('standard.appraisal.line',
                                                           'standard_appraisal_employee_line')
    manager_appraisal_line_id = fields.One2many('manager.appraisal.complete', 'manager_appraisal_line_for_employee')
    appraisal_result = fields.Many2one('appraisal.result', tracking=True)
    appraisal_type = fields.Selection(selection=[('performance', 'Performance'),
                                       ('trial', 'Trial Period'),
                                       ('training', 'Training'),
                                       ('mission', 'Mission'),
                                       ('general', 'General'),
                                       ('other', 'Other')], string='Appraisal Type')
    company_id = fields.Many2one('res.company', string='Company',default=lambda self: self.env.company)
    employee_protest = fields.Boolean(
        readonly=True
    )


    @api.onchange('appraisal_plan_id')
    def onchange_applicants(self):
        self.standard_appraisal_employee_line_ids = False
        self.manager_appraisal_line_id = False
        self.fill_employee_or_manager_appraisal()

    # Fill Employee appraisal or manager appraisal

    @api.depends('appraisal_plan_id')
    def fill_employee_or_manager_appraisal(self):
        for item in self:
            # item.start_compute = 0
            # Initialize values
            standard_appraisal_list = []
            manager_appraisal_list = []

            if item.appraisal_plan_id:
                if not item.appraisal_plan_id.is_manager:

                    # Fill standard_appraisal_employee_line_ids to complete
                    for line in item.appraisal_plan_id.standard_appraisal_id:
                        standard_appraisal_list.append({
                            'great_level': line.great_level,
                            'question': line.question
                        })
                    item.standard_appraisal_employee_line_ids = [Command.create(value) for value in standard_appraisal_list]

                else:
                    # Fill manager_appraisal_line_id to complete
                    for line in item.appraisal_plan_id.manager_appraisal_id:
                        complete_manager_appraisal_list = []

                        for record in line.question_id.customize_appraisal_id:
                            complete_manager_appraisal_list.append({
                                'question': record.question,
                                'great_degree_level': record.great_degree_level,
                                'degree_id': record.degree_id.id
                            })

                        manager_appraisal_list.append({
                            'appraisal_name': line.appraisal_name,
                            'question_id': line.question_id.id,
                            'question_complete_id': self.env['manager.appraisal.complete.line'].create({
                                'name': line.question_id.name,
                                'customize_appraisal_id': [Command.create(value) for value in complete_manager_appraisal_list]

                            }).id
                        })
                    item.manager_appraisal_line_id = [Command.create(value) for value in manager_appraisal_list]

                    # Compute total and great level for every line in manager appraisal
                    # compute great level from all lines
                    great_achieved = 0.0
                    for element in item.manager_appraisal_line_id:
                        great_level = 0.0
                        total = 0.0

                        for line in element.question_complete_id.customize_appraisal_id:
                            great_level += line.great_degree_level
                            total += line.greed

                        element.question_complete_id.great_level = great_level
                        element.question_complete_id.total = total

                        # compute great level from all line
                        great_achieved += element.great_level
                    item.great_level = great_achieved

    # Update level achieved values when changed in lines
    # Update level achieved percentage when changed in lines
    # Determine which appraisal result from appraisal percentage
    @api.onchange('standard_appraisal_employee_line_ids', 'appraisal_plan_id')
    def onchange_appraisal_lines(self):
        for item in self:
            # Initialize
            total_greed, total_great_level, item.level_achieved, item.great_level, item.level_achieved_percentage = \
                0.0, 0.0, 0.0, 0.0, 0.0
            appraisal_result_list = []

            if not item.is_manager:
                for line in item.standard_appraisal_employee_line_ids:
                    # Update level achieved values when changed in lines
                    total_greed += line.greed
                    total_great_level += line.great_level
                item.great_level = total_great_level
                item.level_achieved = total_greed

                # Update level achieved percentage when changed in lines
                if item.level_achieved > 0.0 and item.great_level > 0.0:
                    item.level_achieved_percentage = (item.level_achieved * 100) / item.great_level

                if item.level_achieved_percentage > 100:
                    raise exceptions.UserError(
                        _('There is No percentage result for employee appraisal Greater than 100%'))
                # Determine which appraisal result from appraisal percentage
                appraisal_result = self.appraisal_plan_id.mapped('appraisal_result_ids').filtered(lambda
                                                                                                      r: r.result_from <= item.level_achieved_percentage and r.result_to >= item.level_achieved_percentage)
                if len(appraisal_result) > 1:
                    for line in appraisal_result:
                        appraisal_result_list.append(line.name)
                    raise exceptions.UserError(
                        _('Please check appraisal result configuration , there is more than result for percentage %s  are %s ') % (
                            round(item.level_achieved_percentage, 2), appraisal_result_list))
                else:
                    item.appraisal_result = appraisal_result.id

    @api.onchange('manager_appraisal_line_id', 'appraisal_plan_id')
    def onchange_manager_appraisal_line_id(self):
        for item in self:
            # Initialize
            total_greed, total_great_level, item.level_achieved, item.great_level, item.level_achieved_percentage = \
                0.0, 0.0, 0.0, 0.0, 0.0
            appraisal_result_list = []

            if item.is_manager:
                for line in item.manager_appraisal_line_id:
                    # Update level achieved values when changed in lines
                    total_greed += line.total
                    total_great_level += line.great_level
                item.great_level = total_great_level
                item.level_achieved = total_greed

                # Update level achieved percentage when changed in lines
                if item.level_achieved > 0.0 and item.great_level > 0.0:
                    item.level_achieved_percentage = (item.level_achieved * 100) / item.great_level

                if item.level_achieved_percentage > 100:
                    raise exceptions.UserError(
                        _('There is No percentage result for employee appraisal Greater than 100%'))
                # Determine which appraisal result from appraisal percentage
                appraisal_result = self.appraisal_plan_id.mapped('appraisal_result_ids').filtered(lambda
                                                                                                      r: r.result_from <= item.level_achieved_percentage and r.result_to >= item.level_achieved_percentage)
                if len(appraisal_result) > 1:
                    for line in appraisal_result:
                        appraisal_result_list.append(line.name)
                    raise exceptions.UserError(
                        _('Please check appraisal result configuration , there is more than result for '
                          'percentage %s  are %s ') % (round(item.level_achieved_percentage, 2), appraisal_result_list))
                else:
                    item.appraisal_result = appraisal_result.id

    # Recompute function

    def recompute_values_level_achieved(self):
        if not self.is_manager:
            self.onchange_appraisal_lines()
        else:
            self.onchange_manager_appraisal_line_id()

    def action_employee_approve(self):
        if self.employee_id.user_id.id != self.env.user.id:
            raise exceptions.AccessError(
                _('Only %s should approve this appraisal!') % self.employee_id.user_id.name
            )
        self.state = 'state_done'
    
    def action_employee_reject(self):
        if self.employee_id.user_id.id != self.env.user.id:
            raise exceptions.AccessError(
                _('Only %s should reject this appraisal!') % self.employee_id.user_id.name
            )
        self.state = 'employee_rejection'

    def draft(self):
        for item in self:
            if item.employee_appraisal:
               if item.employee_appraisal.state not in ('draft', 'gen_appraisal', 'start_appraisal'):
                  raise exceptions.UserError(_('You can not Re-draft when there is appraisal not in state '
                                           'draft for employees.'))

            if item.employee_id.contract_id.appraisal_result_id:
                item.employee_id.contract_id.appraisal_result_id = False
        self.state = 'draft'

    def set_state_done(self):
        if self.appraisal_type != 'training' and self.employee_id.user_id == self.env.user:
            raise exceptions.UserError(
                _('The evaluation is approved by the direct manager "%s" If you have any problems, contact your manager.'))
        else:
            for item in self:
                if item.employee_protest and item.state == 'draft':
                    item.state = 'employee_confirmation'
                else:
                    # Update appraisal result in contract
                    if item.employee_id.contract_id:
                        if item.appraisal_result:
                            item.employee_id.contract_id.appraisal_result_id = item.appraisal_result
                    else:
                        raise exceptions.UserError(
                            _('There is no contract for employee "%s" to update appraisal result ') % item.employee_id.name)
                    item.state = 'state_done'


    def closed(self):
        self.state = 'closed'

    # Override unlink function
    def unlink(self):
        for item in self:
            if item.state != 'draft':
                raise exceptions.UserError(
                    _('You can not delete record in state not in draft for employee "%s" ') % item.employee_id.name)
        return super(Appraisal, self).unlink()


class StandardAppraisalLines(models.Model):
    _name = 'standard.appraisal.line'
    _rec_name = 'greed'
    _description = 'Standard Appraisal line'
    standard_appraisal_employee_line = fields.Many2one('hr.employee.appraisal')

    greed = fields.Float()
    accrued_greed = fields.Float()
    question = fields.Char()
    great_level = fields.Float()
    priority = fields.Selection(
        [   ('0', '0'),
            ('1', '1'),
            ('2', '2'),
            ('3', '3'),
            ('4', '4'),
            ('5', '5'),
        ],
        string='Priority',
        store=True
    )
    from_greed = fields.Boolean(default=False)
    from_priority = fields.Boolean(default=False)

    @api.onchange('greed', 'great_level')
    def _compute_priority(self):
        for rec in self:
            if rec.from_priority:
                # Reset and skip to avoid circular trigger
                rec.from_priority = False
                continue
            rec.from_greed = True
            if rec.great_level:
                value = rec.greed / (rec.great_level / 5)
                if 1 <= value < 2:
                    rec.priority = '1'
                elif 2 <= value < 3:
                    rec.priority = '2'
                elif 3 <= value < 4:
                    rec.priority = '3'
                elif 4 <= value < 5:
                    rec.priority = '4'
                elif value >= 5:
                    rec.priority = '5'
                else:
                    rec.priority = '0'
            else:
                rec.priority = '0'

    @api.onchange('priority')
    def _inverse_priority(self):
        for rec in self:
            if rec.from_greed:
                # Reset and skip to avoid circular trigger
                rec.from_greed = False
                continue
            rec.from_priority = True
            if rec.great_level and rec.priority:
                rec.greed = int(rec.priority) * (rec.great_level / 5)

    @api.constrains('greed', 'great_level')
    def greed_constrains(self):
        for item in self:
            if item.greed > item.great_level:
                raise exceptions.UserError(_('You Cannot The Greed Greater Than Great Level'))
    
    def write(self, vals):
        for rec in self:
            if rec.standard_appraisal_employee_line.state == 'employee_confirmation' and (not 'accrued_greed' in vals or len(vals.items()) > 1):
                raise exceptions.AccessError(
                    _('You are only allowed to change accrued greed in this state!')
                )
        return super(StandardAppraisalLines, self).write(vals)


class ManagerAppraisalLines(models.Model):
    _name = 'manager.appraisal.complete'

    appraisal_name = fields.Char()
    great_level = fields.Float(related='question_complete_id.great_level')
    total = fields.Float(related='question_complete_id.total')

    # Relational fields
    question_id = fields.Many2one('manager.appraisal.line')
    question_complete_id = fields.Many2one('manager.appraisal.complete.line', domain="[('id','in',[])]")
    manager_appraisal_line_for_employee = fields.Many2one('hr.employee.appraisal')  # inverse field

    # Open manager appraisal

    def open_manager_appraisal(self):
        for item in self:
            if item.question_complete_id:
                action = self.env.ref('exp_hr_appraisal.manager_appraisal_complete_line_action').read()[0]
                action['views'] = [(self.env.ref('exp_hr_appraisal.manager_appraisal_complete_line_form_view').id, 'form')]
                action['res_id'] = item.question_complete_id.id
                action['target'] = 'new'
                action['context'] = {"default_has_employee_protest": item.manager_appraisal_line_for_employee.employee_protest}
                return action


class ManagerAppraisalCompleteLines(models.Model):
    _name = 'manager.appraisal.complete.line'

    name = fields.Char()
    total = fields.Float()
    great_level = fields.Float()

    # Relational fields
    customize_appraisal_id = fields.One2many('customize.complete.appraisal', 'customize_appraisal_line_id')
    # FIXME this relation (related_manager_appraisal_complete, question_complete_id) is reversed: the parent should have one2many and here many2one
    related_manager_appraisal_complete = fields.One2many('manager.appraisal.complete', 'question_complete_id')
    has_employee_protest = fields.Boolean(related="related_manager_appraisal_complete.manager_appraisal_line_for_employee.employee_protest")


    @api.onchange('customize_appraisal_id')
    def compute_total_and_greate_level(self):
        # Initialize variables
        total, great_level, self.total, self.great_level = 0.0, 0.0, 0.0, 0.0

        for line in self.customize_appraisal_id:
            great_level += line.great_degree_level
            total += line.greed
        self.great_level = great_level
        self.total = total


class CustomizeAppraisal(models.Model):
    _name = 'customize.complete.appraisal'
    _rec_name = 'question'
    _description = 'Customize complete Appraisal'

    question = fields.Char()
    greed = fields.Float(related='degree_id.greed')
    great_degree_level = fields.Float(related='degree_id.great_degree_level')

    # Relational fields
    customize_appraisal_line_id = fields.Many2one('manager.appraisal.complete.line')  # Inverse Field
    degree_id = fields.Many2one('appraisal.degree')
    accrued_degree_id = fields.Many2one('appraisal.degree')

    def write(self, vals):
        for rec in self:
            if rec.customize_appraisal_line_id.related_manager_appraisal_complete.manager_appraisal_line_for_employee.state == 'employee_confirmation' and (not 'accrued_degree_id' in vals or len(vals.items()) > 1):
                raise exceptions.AccessError(
                    _('You are only allowed to change accrued greed in this state!')
                )
        return super(CustomizeAppraisal, self).write(vals)