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

from datetime import datetime

from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools.safe_eval import safe_eval

class HrSalaryRuleAccount(models.Model):
    _name = 'hr.salary.rule.account'
    _description = 'Salary Rule Account Mapping'

    rule_id = fields.Many2one('hr.salary.rule', string="Salary Rule", required=True, ondelete="cascade")
    emp_type_id = fields.Many2one('hr.contract.type', string="Employee Type", required=True)
    credit_account_id = fields.Many2one('account.account', string="Credit Account", required=True)
    debit_account_id = fields.Many2one('account.account', string="Debit Account", required=True)



class HrSalaryRules(models.Model):
    _inherit = 'hr.salary.rule'

    start_date = fields.Date(string='Start Date', default=fields.date.today())
    end_date = fields.Date(string='End Date')
    salary_type = fields.Selection([
        ('fixed', 'Fixed for all'),
        ('related_levels', 'Related with Levels'),
        ('related_groups', 'Related with Groups'),
        ('related_degrees', 'Related with Degrees')
    ], default="fixed", string='Type Scale')
    related_qualifications = fields.Boolean(string='Related with qualifications')
    special = fields.Boolean(string='Special')
    reduce_with_leaves = fields.Boolean(string='Reduce With Leaves',default=True)
    min_leave_days_to_deduct = fields.Integer(string='Min Leave Days To Deduct')
    company_id = fields.Many2one(comodel_name='res.company', string='Company', required=True,
                                 default=lambda self: self.env.user.company_id)
    discount_absence = fields.Selection([
        ('by_day', 'By Day'),
        ('by_hour', 'By Hour'),
        ('no_discount', 'No discount')
    ], default="no_discount", string='Discount Absence')
    fixed_amount = fields.Integer(string='Fixed Amount')

    # relational fields
    related_benefits_discounts = fields.Many2many(comodel_name='hr.salary.rule',
                                                  relation='salary_rule_benefit_discount_rel',
                                                  column1='rule_id', column2='sub_rule_id',
                                                  string='Related Benefits and Discount')
    salary_amount_ids = fields.One2many('related.salary.amount', 'salary_rule_id')
    rule_credit_account_id = fields.Many2one('account.account')
    rule_debit_account_id = fields.Many2one('account.account')
    rules_type = fields.Selection([
        ('salary', 'Salary Allowance'),
        ('house', 'House Allowance'),
        ('overtime', 'Overtime Allowance'),
        ('mandate', 'Mandate Allowance'),
        ('transport', 'Transport Allowance'),
        ('termination', 'End Of Services'),
        ('insurnce', 'Insurnce Deduction'),
        ('other', 'Other')
    ], string='Rules Type')

    account_ids = fields.One2many('hr.salary.rule.account', 'rule_id')
    transfer_by_emp_type = fields.Boolean('Transfer By Employee Type')

    def get_debit_account_id(self, emp_type):
        if not self.transfer_by_emp_type:  return self.rule_debit_account_id.id
        account_mapping = self.sudo().account_ids.filtered(lambda a: a.emp_type_id.id == emp_type)
        return account_mapping[0].debit_account_id.id if account_mapping else False

    def get_credit_account_id(self, emp_type):
        if not self.transfer_by_emp_type:  return self.rule_credit_account_id.id
        account_mapping = self.sudo().account_ids.filtered(lambda a: a.emp_type_id.id == emp_type)
        return account_mapping[0].credit_account_id.id if account_mapping else False

    @api.constrains('rules_type', 'category_id')
    def _check_dates(self):
        for rec in self:
            if rec.category_id.rule_type != 'deduction' and rec.rules_type == 'insurnce':
                raise UserError(_("The Salary Rule is Not Deduction"))

    # Override function compute rule in hr salary rule

    def _compute_rule(self, localdict):
        payslip = localdict.get('payslip')
        contract = localdict.get('contract')
        if self.amount_select == 'percentage':
            total_percent, total = 0, 0
            if self.related_benefits_discounts:
                for line in self.related_benefits_discounts:
                    calc_line = line._compute_rule(localdict)[0]

                    if line.amount_select == 'fix':
                        if contract.advantages:
                            for con in contract.advantages:
                                if line.id == con.benefits_discounts.id:
                                    if payslip:
                                        if con.date_from > payslip.date_from:
                                            total_percent = calc_line
                                        elif con.date_to is not None and str(
                                                con.date_to) >= payslip.date_to or con.date_to is None:
                                            if con.type == 'exception':
                                                if con.amount > calc_line or con.amount == calc_line:
                                                    pass
                                                elif con.amount < calc_line:
                                                    total = calc_line - con.amount
                                            elif con.type == 'customize':
                                                total = con.amount
                                            total_percent += total
                                    else:
                                        if str(con.date_from) < str(datetime.now().date()):
                                            if con.date_to:
                                                if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \
                                                        >= datetime.now().date().month or not con.date_to:
                                                    if con.type == 'exception':
                                                        if con.amount > calc_line or con.amount == calc_line:
                                                            pass
                                                        elif con.amount < calc_line:
                                                            total = calc_line - con.amount
                                                    elif con.type == 'customize':
                                                        total = con.amount
                                                    total_percent += total
                                else:
                                    total_percent = calc_line
                        else:
                            total_percent += calc_line

                    elif line.amount_select == 'percentage':
                        if contract.advantages:
                            for con in contract.advantages:
                                if line.id == con.benefits_discounts.id:
                                    if payslip:
                                        if con.date_from > payslip.date_from:
                                            total_percent = calc_line
                                        elif con.date_to is not None and str(
                                                con.date_to) >= payslip.date_to or con.date_to is None:
                                            if con.type == 'exception':
                                                if con.amount > calc_line or con.amount == calc_line:
                                                    pass
                                                elif con.amount < calc_line:
                                                    total = calc_line - con.amount
                                            elif con.type == 'customize':
                                                total = con.amount
                                            total_percent -= calc_line
                                            total_percent += total
                                    else:
                                        if str(con.date_from) < str(datetime.now().date()):
                                            if con.date_to:
                                                if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \
                                                        >= datetime.now().date().month or not con.date_to:
                                                    if con.type == 'exception':
                                                        if con.amount > calc_line or con.amount == calc_line:
                                                            pass
                                                        elif con.amount < calc_line:
                                                            total = calc_line - con.amount
                                                    elif con.type == 'customize':
                                                        total = con.amount
                                                    total_percent -= calc_line
                                                    total_percent += total
                                else:
                                    if con.type != 'exception':
                                        total_percent += calc_line
                                        break
                        else:
                            total_percent += calc_line

                    else:
                        if contract.advantages:
                            for con in contract.advantages:
                                if line.id == con.benefits_discounts.id:
                                    if payslip:
                                        if con.date_from > payslip.date_from:
                                            total_percent = calc_line
                                        elif con.date_to is not None and  con.date_to >= payslip.date_to or con.date_to is None:
                                            if con.type == 'exception':
                                                if con.amount > calc_line or con.amount == calc_line:
                                                    pass
                                                elif con.amount < calc_line:
                                                    total = calc_line - con.amount
                                            elif con.type == 'customize':
                                                total = con.amount
                                            total_percent = 0
                                            total_percent += total
                                    else:
                                        if con.date_from < (datetime.now().date()):
                                            if con.date_to:
                                                if datetime.strptime(str(con.date_to), "%Y-%m-%d").date().month \
                                                        >= datetime.now().date().month or not con.date_to:
                                                    if con.type == 'exception':
                                                        if con.amount > calc_line or con.amount == calc_line:
                                                            pass
                                                        elif con.amount < calc_line:
                                                            total = calc_line - con.amount
                                                    elif con.type == 'customize':
                                                        total = con.amount
                                                    total_percent = 0
                                                    total_percent += total
                                            else:
                                                if datetime.strptime(str(con.date_from),
                                                                     "%Y-%m-%d").date().month >= datetime.now().date().month:
                                                    if con.type == 'exception':
                                                        if con.amount > calc_line or con.amount == calc_line:
                                                            pass
                                                        elif con.amount < calc_line:
                                                            total = calc_line - con.amount
                                                    elif con.type == 'customize':
                                                        total = con.amount + calc_line
                                                    total_percent = 0
                                                    total_percent += total

                                else:
                                    if not total_percent:
                                        total_percent = calc_line
                        else:
                            total_percent += calc_line
            if total_percent:
                if self.salary_type == 'fixed':
                    try:
                        return float(total_percent * self.amount_percentage / 100), \
                               float(safe_eval(self.quantity, localdict)), self.amount_percentage
                    except:
                        raise UserError(
                            _('Wrong percentage base or quantity defined for salary rule %s (%s).') % (
                                self.name, self.code))
                elif self.salary_type == 'related_levels':
                    levels_ids = self.salary_amount_ids.filtered(
                        lambda item: item.salary_scale_level.id == contract.salary_level.id)
                    if levels_ids:
                        for l in levels_ids:
                            try:
                                return float(l.salary * total_percent / 100), float(
                                    safe_eval(self.quantity, localdict)), 100.0
                            except:
                                raise UserError(
                                    _('Wrong quantity defined for salary rule %s (%s).') % (
                                        self.name, self.code))
                    else:
                        return 0, 0, 0
                elif self.salary_type == 'related_groups':
                    groups_ids = self.salary_amount_ids.filtered(
                        lambda item: item.salary_scale_group.id == contract.salary_group.id)
                    if groups_ids:
                        for g in groups_ids:
                            try:
                                return float(g.salary * total_percent / 100), float(
                                    safe_eval(self.quantity, localdict)), 100.0
                            except:
                                raise UserError(
                                    _('Wrong quantity defined for salary rule %s (%s).') % (
                                        self.name, self.code))
                    else:
                        return 0, 0, 0
                elif self.salary_type == 'related_degrees':
                    degrees_ids = self.salary_amount_ids.filtered(
                        lambda item: item.salary_scale_degree.id == contract.salary_degree.id)
                    if degrees_ids:
                        for d in degrees_ids:
                            try:
                                return float(d.salary * total_percent / 100), float(
                                    safe_eval(self.quantity, localdict)), 100.0
                            except:
                                raise UserError(
                                    _('Wrong quantity defined for salary rule %s (%s).') % (
                                        self.name, self.code))
                    else:
                        return 0, 0, 0
            else:
                try:
                    return 0, 0, 0
                except:
                    raise UserError(_('There is no total for rule : %s') % self.name)

        elif self.amount_select == 'fix':
            if self.salary_type == 'fixed':
                try:
                    return self.fixed_amount, float(safe_eval(self.quantity, localdict)), 100.0
                except:
                    raise UserError(_('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code))
            elif self.salary_type == 'related_levels':
                levels_ids = self.salary_amount_ids.filtered(
                    lambda item: item.salary_scale_level.id == contract.salary_level.id)
                if levels_ids:
                    for l in levels_ids:
                        try:
                            return l.salary, float(safe_eval(self.quantity, localdict)), 100.0
                        except:
                            raise UserError(
                                _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code))
                else:
                    return 0, 0, 0
            elif self.salary_type == 'related_groups':
                groups_ids = self.salary_amount_ids.filtered(
                    lambda item: item.salary_scale_group.id == contract.salary_group.id)
                if groups_ids:
                    for g in groups_ids:
                        try:
                            return g.salary, float(safe_eval(self.quantity, localdict)), 100.0
                        except:
                            raise UserError(
                                _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code))
                else:
                    return 0, 0, 0
            elif self.salary_type == 'related_degrees':
                degrees_ids = self.salary_amount_ids.filtered(
                    lambda item: item.salary_scale_degree.id == contract.salary_degree.id)
                if degrees_ids:
                    for d in degrees_ids:
                        try:
                            return d.salary, float(safe_eval(self.quantity, localdict)), 100.0
                        except:
                            raise UserError(
                                _('Wrong quantity defined for salary rule %s (%s).') % (self.name, self.code))
                else:
                    return 0, 0, 0
            else:
                raise UserError(_('Error, Select Salary type to calculate rule'))

        else:
            try:
                safe_eval(self.amount_python_compute, localdict, mode='exec', nocopy=True)
                return float(localdict['result']), 'result_qty' in localdict and localdict[
                    'result_qty'] or 1.0, 'result_rate' in localdict and localdict['result_rate'] or 100.0
            except:
                raise UserError(_('Wrong python code defined for salary rule %s (%s).') % (self.name, self.code))


class SalaryConfig(models.Model):
    _name = 'related.salary.amount'

    salary_scale = fields.Many2one('hr.payroll.structure')
    salary_scale_level = fields.Many2one('hr.payroll.structure')
    salary_scale_group = fields.Many2one('hr.payroll.structure')
    salary_scale_degree = fields.Many2one('hr.payroll.structure')
    salary = fields.Float(string='Salary / Percentage')

    # relations fields
    salary_rule_id = fields.Many2one(comodel_name='hr.salary.rule')

    # filter salary_level,salary_group,salary_degree

    @api.onchange('salary_scale')
    def onchange_salary_scale(self):
        for item in self:
            if item.salary_scale:
                level_ids = self.env['hr.payroll.structure'].search(
                    [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'level')])
                group_ids = self.env['hr.payroll.structure'].search(
                    [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'group')])
                degree_ids = self.env['hr.payroll.structure'].search(
                    [('salary_scale_id', '=', item.salary_scale.id), ('type', '=', 'degree')])
                return {'domain': {'salary_scale_level': [('id', 'in', level_ids.ids)],
                                   'salary_scale_group': [('id', 'in', group_ids.ids)],
                                   'salary_scale_degree': [('id', 'in', degree_ids.ids)]}}
            else:
                return {'domain': {'salary_scale_level': [('id', 'in', [])],
                                   'salary_scale_group': [('id', 'in', [])],
                                   'salary_scale_degree': [('id', 'in', [])]}}

    # filter depend on salary_level

    @api.onchange('salary_scale_level')
    def onchange_salary_level(self):
        for item in self:
            if item.salary_scale_level:
                group_ids = self.env['hr.payroll.structure'].search(
                    [('salary_scale_level_id', '=', item.salary_scale_level.id), ('type', '=', 'group')])
                return {'domain': {'salary_scale_group': [('id', 'in', group_ids.ids)],
                                   'salary_scale_degree': [('id', 'in', [])]}}
            else:
                return {'domain': {'salary_scale_group': [('id', 'in', [])],
                                   'salary_scale_degree': [('id', 'in', [])]}}

    # filter depend on salary_group

    @api.onchange('salary_scale_group')
    def onchange_salary_group(self):
        for item in self:
            if item.salary_scale_group:
                degree_ids = self.env['hr.payroll.structure'].search(
                    [('salary_scale_group_id', '=', item.salary_scale_group.id), ('type', '=', 'degree')])
                return {'domain': {'salary_scale_degree': [('id', 'in', degree_ids.ids)]}}
            else:
                return {'domain': {'salary_scale_degree': [('id', 'in', [])]}}


class SalaryRuleCategory(models.Model):
    _inherit = 'hr.salary.rule.category'

    rule_type = fields.Selection(selection_add=[
        ('end_of_service', 'End of Service')
    ])
