# -*- coding: utf-8 -*-
from __future__ import division
from datetime import datetime

from odoo import models, fields, api, _
from odoo.exceptions import UserError
import calendar

# import logging


class employee_overtime_request(models.Model):
    _name = 'employee.overtime.request'
    _description = 'Employee Overtime Request'
    _rec_name = 'request_date'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = 'request_date DESC'

    request_date = fields.Date(default=lambda self: fields.Date.today())
    reason = fields.Text()
    date_to = fields.Date()
    date_from = fields.Date()
    transfer_type = fields.Selection([('accounting', 'Accounting'), ('payroll', 'Payroll')], default='payroll')

    overtime_plase = fields.Selection([('inside', 'Inside'), ('outside', 'Outside')])
    state = fields.Selection(
        [('draft', _('Draft')),
         ('submit', _('Waiting Direct Manager')),
         ('direct_manager', _('Waiting Department Manager')),
         ('financial_manager', _('Wait HR Department')),
         ('hr_aaproval', _('Wait Approval')),
         ('executive_office', _('Wait Transfer')),
         ('validated', _('Transferred')),
         ('refused', _('Refused'))], default="draft", tracking=True)

    # Relation fields
    account_id = fields.Many2one(comodel_name='account.account', string='Account')
    journal_id = fields.Many2one(comodel_name='account.journal', string='Payment Method')

    benefits_discounts = fields.Many2one(comodel_name='hr.salary.rule', string='Benefits/Discounts',
                                         domain=[('rules_type', '=', 'overtime')])
    line_ids_over_time = fields.One2many(comodel_name='line.ids.over.time', inverse_name='employee_over_time_id',
                                         tracking=True)

    department_id = fields.Many2one('hr.department')
    employee_id = fields.Many2one('hr.employee', 'Responsible', default=lambda item: item.get_user_id(),
                                  domain=[('state', '=', 'open')])
    manager_id = fields.Many2one('hr.employee', string='Direct Manager', related='employee_id.parent_id', store=True,
                                 readonly=True,
                                 domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
    employee_no = fields.Char(related='employee_id.emp_no', readonly=True,string='Employee Number', store=True)
    exception = fields.Boolean(string="Exception Hours", default=False,
                               help='Exceeding The Limit Of Overtime Hours Per Month')

    company_id = fields.Many2one('res.company',string="Company", default=lambda self: self.env.company)

    is_branch = fields.Many2one(related='department_id.branch_name', store=True, readonly=True)

    def get_user_id(self):
        employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
        if employee_id:
            return employee_id.id
        else:
            return False

    @api.onchange('employee_id')
    def get_department_id(self):
        if self.employee_id:
            self.department_id = self.employee_id.department_id.id

    @api.onchange('request_date','date_from')
    def chick_date_request(self):
        for rec in self:
            days_after = rec.employee_id.contract_id.working_hours.request_after_day
            if days_after > 0 and rec.date_from:
               rec.date_to=False
               date_from = datetime.strptime(str(rec.date_from), "%Y-%m-%d").date()
               request_date = datetime.strptime(str(rec.request_date), "%Y-%m-%d").date()
               last_day_date = date_from.replace(day=calendar.monthrange(date_from.year, date_from.month)[1])
               diff_days = (request_date - last_day_date).days
               if diff_days > days_after:
                  raise UserError(_(
                        'Sorry, Cannot Be Request after %s Days From The End Of The Overtime Month') % days_after)


    #get account and journal from setting
    @api.onchange('transfer_type','employee_id')
    def get_account_ids(self):
        if self.transfer_type == 'accounting':
           self.journal_id = self.employee_id.contract_id.working_hours.journal_overtime_id
           self.account_id = self.employee_id.contract_id.working_hours.account_overtime_id

    @api.onchange('transfer_type', 'account_id', 'journal_id', 'line_ids_over_time')
    def onchange_transfer_type(self):
        if self.transfer_type == 'payroll':
            self.account_id = False
            self.journal_id = False
            for line in self.line_ids_over_time:
                line.account_id = False
                line.journal_id = False
        if self.transfer_type == 'accounting':
            for line in self.line_ids_over_time:
                if self.state == 'hr_aaproval':
                    if not line.account_id:
                        line.account_id = self.account_id
                    if not line.journal_id:
                        line.journal_id = self.journal_id
                #else:
                   # line.account_id = False
                   # line.journal_id = False

    @api.onchange('account_id')
    def onchange_account_id(self):
        for line in self.line_ids_over_time:
            line.account_id = self.account_id

    @api.onchange('journal_id')
    def onchange_journal_id(self):
        for line in self.line_ids_over_time:
            line.journal_id = self.journal_id

    def re_draft(self):
        # when redraft cancel the created account move
        if self.transfer_type == 'payroll':
            for record in self.line_ids_over_time:
                record.advantage_id.draft()
                record.advantage_id.unlink()

            self.state = 'draft'
        if self.transfer_type == 'accounting':
            if self.line_ids_over_time[0].move_id:
                move_id_not_draft = False
                for line in self.line_ids_over_time:
                    if line.move_id.state == 'posted':
                        move_id_not_draft_name = line.move_id.name
                        move_id_not_draft = True
                if move_id_not_draft:
                    raise UserError(_(
                        'You can not cancel account move "%s" in state not draft') % move_id_not_draft_name)
                else:
                    for record in self.line_ids_over_time:
                        # record.move_id.write({'state': 'canceled'})
                        record.move_id.unlink()
                        record.write({
                            'move_id': False
                        })
                        #record.account_id = False
                        #record.journal_id = False
                    self.write({'state': 'draft'})
                    #self.account_id = False
                    #self.journal_id = False
            else:
                self.write({
                    'state': 'draft',
                    #'account_id': False,
                    #'journal_id': False
                })
                for record in self.line_ids_over_time:
                    record.write({
                        'move_id': False,
                        #'account_id': False,
                        #'journal_id': False
                    })

    def submit(self):
        if not self.line_ids_over_time:
                    raise UserError(_('Sorry, Can Not Request without The Employees'))
        self.line_ids_over_time.get_max_remain_hours()
        self.chick_not_mission()
        self.state = "submit"

    def direct_manager(self):
        for rec in self:
            rec.line_ids_over_time.chick_price_hour()
            rec.chick_not_mission()
            manager = rec.sudo().employee_id.parent_id
            hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
            if manager:
               if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
                  rec.write({'state': 'direct_manager'})
               else:
                  raise UserError(_("Sorry, The Approval For The Direct Manager '%s' Only OR HR Manager!")%(rec.employee_id.parent_id.name))
            else:
                rec.write({'state': 'direct_manager'})

    def financial_manager(self):
        for rec in self:
            rec.chick_not_mission()
            manager = rec.sudo().employee_id.coach_id
            hr_manager = rec.sudo().employee_id.company_id.hr_manager_id
            if manager:
               if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
                  rec.write({'state': 'financial_manager'})
               else:
                   raise UserError(_("Sorry, The Approval For The Department Manager '%s' Only OR HR Manager!")%(rec.employee_id.coach_id.name))
            else:
                rec.write({'state': 'financial_manager'})

    def hr_aaproval(self):
        self.chick_not_mission()
        if self.exception == True:
            self.state = "hr_aaproval"
        else:
            self.state = "executive_office"

    def executive_office(self):
        self.chick_not_mission()
        self.state = "executive_office"

    def validated(self):
        if self.transfer_type == 'accounting':
            for item in self:
                for record in item.line_ids_over_time:
                    emp_type = record.employee_id.employee_type_id
                    account_debit_id = record.employee_id.contract_id.working_hours.get_debit_overtim_account_id(emp_type)
                    journal_id = record.employee_id.contract_id.working_hours.journal_overtime_id
                    analytic_account_id = record.employee_id.contract_id.working_hours.analytic_account_id
                    if not analytic_account_id:
                       analytic_account_id = record.employee_id.department_id.analytic_account_id
                    if not journal_id:
                        raise UserError(_('You Must Enter The Journal Name Overtim Setting.'))
                    if not account_debit_id:
                        raise UserError(_('Employee %s, has no Overtime Account Setting Base On Employee Type.'
                                                   ) % record.employee_id.name)
                    debit_line_vals = {
                        'name': record.employee_id.name,
                        'debit': record.price_hour,
                        'account_id': account_debit_id.id,
                        'partner_id': record.employee_id.user_id.partner_id.id,
                        'analytic_account_id': analytic_account_id.id,
                    }
                    credit_line_vals = {
                        'name': record.employee_id.name,
                        'credit': record.price_hour,
                        'account_id': journal_id.default_account_id.id,
                        'partner_id': record.employee_id.user_id.partner_id.id
                    }
                    if not record.move_id:
                       move = record.env['account.move'].create({
                           'state': 'draft',
                           'journal_id': journal_id.id,
                           'date': item.request_date,
                           'ref': record.employee_id.name,
                           'line_ids': [(0, 0, debit_line_vals), (0, 0, credit_line_vals)],
                           'res_model': 'employee.overtime.request',
                           'res_id': self.id
                       })
                       record.account_id = account_debit_id.id
                       record.journal_id = journal_id.id
                       record.move_id = move.id
            self.state = "validated"
        if self.transfer_type == 'payroll':
            # last_day_of_current_month = date.today().replace(day=calendar.monthrange(date.today().year, date.today().month)[1])
            # first_day_of_current_month = date.today().replace(day=1)
            for item in self:
                for record in item.line_ids_over_time:
                    if record.employee_id.contract_id:

                        advantage_arc = record.env['contract.advantage'].create({
                            'benefits_discounts': item.benefits_discounts.id,
                            'type': 'customize',
                            'date_from': item.date_from,
                            'date_to': item.date_to,
                            'amount': record.price_hour,
                            'over_time_id': True,
                            'employee_id': record.employee_id.id,
                            'contract_advantage_id': record.employee_id.contract_id.id,
                            'out_rule': True,
                            'state': 'confirm',
                            'comments': item.reason})
                        record.advantage_id = advantage_arc.id

                    else:
                        raise UserError(_('Employee "%s" has no contract Please create contract to add '
                                                   'line to advantages') % record.employee_id.name)

            self.state = "validated"

    def refused(self):
        self.state = "refused"

    #Refuse For The Direct Manager Only
    def direct_manager_refused(self):
        for rec in self:
            manager = rec.sudo().employee_id.parent_id
            hr_manager = rec.sudo().employee_id.user_id.company_id.hr_manager_id
            if manager:
                if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
                   rec.refused()
                else:
                    raise UserError(_("Sorry, The Refuse For The Direct Manager '%s' Only OR HR Manager!") % (manager.name))
            else:
                 rec.refused()

    #Refuse For The Department Manager Only
    def dep_manager_refused(self):
        for rec in self:
            manager = rec.sudo().employee_id.coach_id
            hr_manager = rec.sudo().employee_id.user_id.company_id.hr_manager_id
            if manager:
                if manager.user_id.id == rec.env.uid or hr_manager.user_id.id == rec.env.uid:
                   rec.refused()
                else:
                    raise UserError(_("Sorry, The Refuse For The Department Manager '%s' Only !")%(rec.employee_id.coach_id.name))
            else:
                 rec.refused()

    def unlink(self):
        for i in self:
            if i.state != 'draft':
                    raise UserError(_('You can not delete record in state not in draft'))
            i.line_ids_over_time.unlink()
        return super(employee_overtime_request, self).unlink()

    @api.onchange('line_ids_over_time.employee_id', 'date_from', 'date_to')
    def chick_not_mission(self):
        for rec in self:
            Module = self.env['ir.module.module'].sudo()
            modules_mission = Module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')])
            if modules_mission:
                if rec.date_to and rec.date_from:
                    if rec.date_to < rec.date_from:
                        raise UserError(_('Date Form Must Be Less than Date To'))
                    for line in rec.line_ids_over_time:
                        clause_1 = ['&', ('official_mission_id.date_from', '<=', rec.date_from),
                                    ('official_mission_id.date_to', '>=', rec.date_from)]
                        clause_2 = ['&', ('official_mission_id.date_from', '<=', rec.date_to),
                                    ('official_mission_id.date_to', '>=', rec.date_to)]
                        clause_3 = ['&', ('official_mission_id.date_from', '>=', rec.date_from),
                                    ('official_mission_id.date_to', '<=', rec.date_to)]
                        mission = self.env['hr.official.mission.employee'].search(
                            [('employee_id', '=', line.employee_id.id), ('official_mission_id.state', '!=', 'refused'),
                             ('official_mission_id.mission_type.related_with_financial', '=', True),
                             #('official_mission_id.mission_type.work_state', '=', 'legation'),
                             '|', '|'] + clause_1 + clause_2 + clause_3)
                        if mission:
                            raise UserError(_('Sorry The Employee %s Actually Has Legation '
                                                       'Amount For this Period') % line.employee_id.name)

    # TOOO DOOO
    @api.onchange('overtime_plase', 'date_from', 'date_to', 'exception')
    def chick_hours_calenders(self):
        self.line_ids_over_time.chick_hours_calender()
        

class HrEmployeeOverTime(models.Model):
    _name = 'line.ids.over.time'
    _description = 'Overtime Line'
    _rec_name = 'employee_id'

    over_time_workdays_hours = fields.Float(string="workdays hours")
    over_time_vacation_hours = fields.Float(string="Vacation days hours")
    daily_hourly_rate = fields.Float(string='Daily Hourly Rate', compute='get_over_time_amount', store=True)
    holiday_hourly_rate = fields.Float(string='Holiday Hourly Rate', compute='get_over_time_amount', store=True)

    price_hour = fields.Float(string='Overtime Amount', compute='get_over_time_amount', store=True)

    # Relational fields
    account_id = fields.Many2one('account.account')
    journal_id = fields.Many2one('account.journal', string='Payment Method', domain=[('type', 'in', ('bank', 'cash'))])
    move_id = fields.Many2one('account.move')
    employee_id = fields.Many2one('hr.employee', string='Employee', required=True)
    employee_over_time_id = fields.Many2one('employee.overtime.request', string='Employee')
    calculate_from_total = fields.Boolean(string="Calculate From Total")
    transfer_type = fields.Selection(related='employee_over_time_id.transfer_type')

    advantage_id = fields.Many2one(comodel_name='contract.advantage', string='Allowance Employee')

    max_hours = fields.Float(compute='get_max_remain_hours', string="Max Hours", store=True)
    remaining_hours = fields.Float(compute='get_max_remain_hours', string="Remaining Hours", store=True)
    #exception = fields.Boolean(string="Exception", default=False)
    exception  = fields.Boolean(related='employee_over_time_id.exception', string="Exception Hours", store=True)
    overtime_plase = fields.Selection(related='employee_over_time_id.overtime_plase', store=True,
                                      string="Overtime Plase")
    state = fields.Selection(related='employee_over_time_id.state', store=True, string="State")

    @api.constrains('price_hour')
    def chick_price_hour(self):
        for rec in self:
            if rec.employee_id:
               if rec.price_hour < 0:
                    raise UserError(_('You can not Request Overtime The Employee %s The Overtime Amount Less Than Zero') % rec.employee_id.name)
               if rec.price_hour == 0:
                    raise UserError(_('You can not Request Overtime The Employee %s The Overtime Amount Zero') % rec.employee_id.name)

    @api.onchange('overtime_plase', 'over_time_workdays_hours', 'over_time_vacation_hours', 'employee_id', 'exception')
    def chick_hours_calender(self):
        for rec in self:
            overtime_day_hour = 0
            overtime_holi_hour = 0
            # day_hour=0.0
            # holiday_hour=0.0
            if rec.employee_id:
                if not rec.employee_id.first_hiring_date:
                    raise UserError(
                        _('You can not Request Overtime The Employee %s have Not First Hiring Date') % rec.employee_id.name)
            if rec.overtime_plase == 'inside' and rec.employee_id:
                attendance_transaction = self.env['hr.attendance.transaction'].search(
                    [('employee_id', '=', rec.employee_id.id),
                     ('date', '>=', rec.employee_over_time_id.date_from),
                     ('date', '<=', rec.employee_over_time_id.date_to)])
                for tran in attendance_transaction:
                    if tran.additional_hours > 0 and not tran.public_holiday:
                        overtime_day_hour += tran.additional_hours
                    if tran.public_holiday:
                        overtime_holi_hour += tran.office_hours
                if rec.over_time_workdays_hours > overtime_day_hour and not rec.exception:
                    raise UserError(
                        _('The Overtime Workdays Employee %s Exceeded The Attendance workdays '
                          'Hours') % rec.employee_id.name)

                if rec.over_time_vacation_hours > overtime_holi_hour and not rec.exception:
                    raise UserError(
                        _('The Overtime Publice Holiday Employee %s Exceeded The Attendance Public '
                          'Holiday Hours') % rec.employee_id.name)



    # dynamic domain on employee_id
    # Select employee once in Over Time Line
    @api.onchange('employee_id')
    def get_emplyee_id_domain(self):
        # Check if employee selected once
        if self.employee_over_time_id.department_id:
            # for dep in self.official_mission_id.department_id:
            employee_id = self.env['hr.employee'].search(
                [('department_id', '=', self.employee_over_time_id.department_id.id), ('state', '=', 'open')]).ids
            if employee_id:
                for line in self.employee_over_time_id.line_ids_over_time:
                    if line.employee_id:
                        if line.employee_id.id in employee_id:
                            employee_id.remove(line.employee_id.id)
                return {'domain': {'employee_id': [('id', 'in', employee_id)]}}
        else:
            employee_id = self.env['hr.employee'].search([('state', '=', 'open')]).ids
            if employee_id:
                for line in self.employee_over_time_id.line_ids_over_time:
                    if line.employee_id:
                        if line.employee_id.id in employee_id:
                            employee_id.remove(line.employee_id.id)
                return {'domain': {'employee_id': [('id', 'in', employee_id)]}}

    '''@api.model
    def default_get(self, fields):
        res = super(HrEmployeeOverTime, self).default_get(fields)
        if self._context.get('account_id') and self._context.get('journal_id'):
            res['account_id'] = self._context.get('account_id')
            res['journal_id'] = self._context.get('journal_id')
        return res'''

    @api.depends('employee_id.contract_id', 'over_time_workdays_hours', 'over_time_vacation_hours', 'employee_id',
                 'calculate_from_total')
    def get_over_time_amount(self):
        for line in self:
            contract_id = line.employee_id.contract_id
            if contract_id.working_hours:
                wage_daily = contract_id.total_allowance + (
                        contract_id.salary * contract_id.working_hours.overtime_factor_daily)
                # 1/2 Basic salary + total Salaey
                wage_holiday = contract_id.total_allowance + (
                        contract_id.salary * contract_id.working_hours.overtime_factor_holiday)
                # 1 Basic salary + total Salaey
                total_hours = contract_id.working_hours.work_days * contract_id.working_hours.work_hour
                # 240 per month
                if total_hours != 0:

                    price_hour_daily = wage_daily / total_hours
                    price_hour_holiday = wage_holiday / total_hours
                    # if line.over_time_workdays_hours > 0 or line.over_time_vacation_hours > 0:
                    if line.employee_id:
                        line.daily_hourly_rate = price_hour_daily
                        o_t_a_d = price_hour_daily * line.over_time_workdays_hours

                        line.holiday_hourly_rate = price_hour_holiday
                        o_t_a_v = price_hour_holiday * line.over_time_vacation_hours
                        line.price_hour = o_t_a_d + o_t_a_v

                        emp_total_hours = line.over_time_workdays_hours + line.over_time_vacation_hours

                        # if emp_total_hours > contract_id.working_hours.max_overtime_hour:
                        # raise exceptions.UserError(_('The Number Of Overtime Hours For The Employee %s Is Greater
                        # Max Hours per Month')% line.employee_id.name)

                else:
                    raise UserError(_('The Number Of Overtime Hours And Days is Missing'))
            # else:
            #     line.daily_hourly_rate = 0
            #     line.holiday_hourly_rate = 0
            #
            #     line.price_hour = 0

    @api.depends('employee_id.contract_id', 'employee_id', 'employee_over_time_id.date_from',
                 'employee_over_time_id.date_to','over_time_workdays_hours','over_time_vacation_hours')
    def get_max_remain_hours(self):
        for line in self:
            contract_id = line.employee_id.contract_id
            if contract_id.working_hours:
                line.max_hours = contract_id.working_hours.max_overtime_hour
                if line.employee_over_time_id.date_to and line.employee_over_time_id.date_from:
                    month_current_from = datetime.strptime(str(line.employee_over_time_id.date_from),
                                                           '%Y-%m-%d').strftime('%m')
                    year_current_from = datetime.strptime(str(line.employee_over_time_id.date_from),
                                                          '%Y-%m-%d').strftime('%y')
                    month_current_to = datetime.strptime(str(line.employee_over_time_id.date_to),
                                                         '%Y-%m-%d').strftime('%m')
                    year_current_to = datetime.strptime(str(line.employee_over_time_id.date_to),
                                                        '%Y-%m-%d').strftime('%y')
                    if month_current_from != month_current_to or year_current_from != year_current_to:
                        raise UserError(
                            _('Sorry, The overtime period Must be During the same Month for the Year'))
                    overtime_ids = self.search(
                        [('employee_id', '=', line.employee_id.id), ('employee_over_time_id.state', '!=', 'refused')])
                    total_hours = 0.0
                    remaining_hours = line.max_hours
                    for rec in overtime_ids:
                        month_previous = datetime.strptime(str(rec.employee_over_time_id.date_from),
                                                           '%Y-%m-%d').strftime('%m')
                        year_previous = datetime.strptime(str(rec.employee_over_time_id.date_from),
                                                          '%Y-%m-%d').strftime('%y')
                        if month_current_from == month_previous and year_current_from == year_previous:
                            total_hours += rec.over_time_workdays_hours + rec.over_time_vacation_hours
                            remaining_hours = line.max_hours - total_hours
                    line.remaining_hours = remaining_hours
                    if remaining_hours < 0 and line.exception == False:
                        raise  UserError(
                            _('The Number Of Overtime Hours For The Employee %s Is Greater Max Hours per '
                              'Month') % line.employee_id.name)

    def unlink(self):
        for i in self:
            if i.state != 'draft':
                    raise UserError(_('You can not delete record in state not in draft'))
        return super(HrEmployeeOverTime, self).unlink()
