# -*- coding: utf-8 -*-
# Copyright (C) 2018 Compassion CH
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import pytz

from odoo import models, fields, api

_logger = logging.getLogger(__name__)


class HrAttendanceDay(models.Model):
    """
    The instances of hr.attendance.day is created either at the first
    attendance of the day or by the method
    hr.employee._cron_create_attendance() called by a cron everyday.
    """
    _name = "hr.attendance.day"
    _description = "Attendance day"
    _order = 'date DESC'
    _sql_constraints = [('unique_attendance_day', 'unique(date, employee_id)',
                         'This "Attendance day" already exists for this '
                         'employee')]

    ##########################################################################
    #                                 FIELDS                                 #
    ##########################################################################
    date = fields.Date(required=True, default=fields.Date.today())
    employee_id = fields.Many2one(
        'hr.employee', "Employee", ondelete="cascade", required=True,
        default=lambda self: self.env.user.employee_ids[0].id)

    # Working schedule
    working_schedule_id = fields.Many2one('resource.calendar', store=True,
                                          string='Working schedule',
                                          compute='_compute_working_schedule',
                                          inverse='_inverse_working_schedule')
    cal_att_ids = fields.Many2many('resource.calendar.attendance', store=True,
                                   compute='_compute_cal_att_ids')
    working_day = fields.Char(compute='_compute_working_day',
                              readonly=True, store=True)
    name = fields.Char(compute='_compute_name', store=True)

    # Leaves
    leave_ids = fields.Many2many('hr.holidays', string='Leaves')
    # todo replace by employee_id.is_absent_totay
    in_leave = fields.Boolean('In leave', compute='_compute_in_leave',
                              store=True)
    public_holiday_id = fields.Many2one('hr.holidays.public.line',
                                        'Public holidays')

    # Due hours
    due_hours = fields.Float('Due hours', compute='_compute_due_hours',
                             readonly=True, store=True)

    # Attendances
    attendance_ids = fields.One2many('hr.attendance', 'attendance_day_id',
                                     'Attendances', readonly=True)

    has_change_day_request = fields.Boolean(
        compute='_compute_has_change_day_request', store=True,
        oldname='has_linked_change_day_request'
    )

    # Worked
    paid_hours = fields.Float(
        compute='_compute_paid_hours', store=True, readonly=True
    )
    fr
ee_breaks_hours = fields.Float(compute='_compute_free_break_hours')
    total_attendance = fields.Float(
        compute='_compute_total_attendance', store=True,
        help='Sum of all attendances of the day'
    )

    coefficient = fields.Float(help='Worked hours coefficient')

    # Break
    due_break_min = fields.Float('Minimum break due',
                                 compute='_compute_due_break')
    due_break_
total = fields.Float('Total break due',
                                   compute='_compute_due_break')
    break_ids = fields.One2many('hr.attendance.break',
                                'attendance_day_id',
                                'Breaks',
                                readonly=True)
    break_total = fields.Float('Total break',
                               compute='_compute_break_total',
                               store=True)
    rule_id = fields.Many2one('hr.attendance.rules', 'Rules',
                              compute='_compute_rule_id')

    day_balance = fields.Float("Day balance",
                               compute='_compute_day_balance',
                               store=True)

    ##########################################################################
    #                             FIELDS METHODS                             #
    ##########################################################################
    @api.multi
    @api.depends('attendance_ids')
    def _compute_working_schedule(self):
        for att_day in self:
            # Find the correspondent resource.calendar
            # First check if the attendance has already a resource.calendar
            schedule = att_day.attendance_ids.mapped('working_schedule_id')
            if schedule:
                # if there is more than one resource.calendar take one...
                schedule = schedule[0]
            else:
                # look for a valid contract...
                # todo: check if att_day.employee_id.current_contract is enough
                contracts = self.env['hr.contract'].search([
                    ('employee_id', '=', att_day.employee_id.id),
                    ('date_start', '<=', att_day.date),
                    '|', ('date_end', '=', False),
                    ('date_end', '>=', att_day.date)
                ], order='date_start desc', limit=1)
                # ...or take the resource.calendar of employee
                schedule = contracts.working_hours or (
                    att_day.employee_id.calendar_id)

            att_day.working_schedule_id = schedule

    def _inverse_working_schedule(self):
        for att_day in self:
            for att in self.attendance_ids:
                att.working_schedule_id = att_day.working_schedule_id

    @api.multi
    @api.depends('working_schedule_id', 'working_schedule_id.attendance_ids')
    def _compute_cal_att_ids(self):
        """
        Find the resource.calendar.attendance matching
        """
        for att_day in self:
            week_day = fields.Date.from_string(att_day.date).weekday()

            # select the calendar attendance(s) that are valid today.
            current_cal_att = att_day.working_schedule_id.mapped(
                'attendance_ids').filtered(
                lambda a: int(a.dayofweek) == week_day)

            # Specific period
            att_schedule = current_cal_att.filtered(
                lambda r: r.date_from is not False and
                r.date_to is not False and
                r.date_from <= att_day.date <= r.date_to)

            # Period with only date_to or date_from
            if not att_schedule:
                att_schedule = current_cal_att.filtered(
                    lambda r:
                    (r.date_from <= att_day.date and not r.date_to and r.date_from) or
                    (r.date_to >= att_day.date and not r.date_from and r.date_to))

            # Default schedule
            if not att_schedule:
                att_schedule = current_cal_att.filtered(
                    lambda r: not r.date_from and not r.date_to)

            att_day.cal_att_ids = att_schedule

    @api.multi
    def get_related_forced_due_hours(self):
        self.ensure_one()
        return self.env['hr.forced.due.hours'].search([
            ('employee_id', '=', self.employee_id.id),
            ('date', '=', self.date)])

    @api.multi
    @api.depends('due_hours')
    def _compute_has_change_day_request(self):
        for att_day in self:
            res = att_day.get_related_forced_due_hours()
            att_day.has_change_day_request = len(res) == 1

    @api.multi
    @api.depends('date')
    def _compute_working_day(self):
        for att_day in self:
            att_day.working_day = fields.Date.from_string(
                att_day.date).strftime('%A').title()

    @api.multi
    @api.depends('working_day')
    def _compute_name(self):
        for rd in self:
            rd.name = rd.working_day + ' ' + rd.date

    @api.multi
    @api.depends('leave_ids', 'public_holiday_id')
    def _compute_in_leave(self):
        for att_day in self:
            att_day.in_leave = att_day.leave_ids or att_day.public_holiday_id

    @api.multi
    @api.depends('working_schedule_id', 'leave_ids', 'public_holiday_id',
                 'cal_att_ids.due_hours')
    def _compute_due_hours(self):
        """First