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

from odoo import api, fields, models, _
from odoo.exceptions import UserError, AccessError, ValidationError
import datetime
from pytz import timezone, UTC

class Project(models.Model):
    _inherit = 'project.project'

    bill_type = fields.Selection([
        ('customer_task', 'Invoice tasks separately to different clients'),
        ('customer_project', 'Invoice all tasks to a single client')
    ], string="Client Type", default="customer_project",
        help='When billing tasks individually, a Sales Order will be created from each task. It is perfect if you would like to bill different services to different clients at different rates. \n When billing the whole project, a Sales Order will be created from the project instead. This option is better if you would like to bill all the tasks of a given project to a specific client either at a fixed rate, or at an employee rate.')

    pricing_type = fields.Selection([
        ('fixed_rate', 'Project that have no time limit'),
        ('employee_rate', 'Consultant rate')
    ], string="Pricing", default="fixed_rate",
        help='The fixed rate is perfect if you bill a service at a fixed rate per hour or day worked regardless of the consultant who performed it. The consultant rate is preferable if your employees deliver the same service at a different rate. For instance, junior and senior consultants would deliver the same service (= consultancy), but at a different rate because of their level of seniority.')

    project_type = fields.Selection([
        ('hours_in_consultant', 'Hours are budgeted according to a consultant'),
        ('hours_no_limit', 'Total hours are budgeted without division to consultant'),
    ], string="Project Type", default="hours_in_consultant")

    privacy_visibility = fields.Selection([
        ('followers', 'Invited internal users'),
        ('employees', 'All internal users'),
        ('portal', 'Invited portal users and all internal users'),
    ],
        string='Visibility', required=True,
        default='followers',
        help="Defines the visibility of the tasks of the project:\n"
             "- Invited internal users: employees may only see the followed project and tasks.\n"
             "- All internal users: employees may see all project and tasks.\n"
             "- Invited portal and all internal users: employees may see everything."
             "   Portal users may see project and tasks followed by\n"
             "   them or by someone of their company.")
    allow_billable = fields.Boolean("Billable", default=True, help="Invoice your time and material from tasks.")

    date_start = fields.Date(string='Start Date')
    date = fields.Date(string='End Date', index=True, tracking=True)
    timesheet_hour = fields.Float(string='Total Timesheet Hours', compute='_compute_calc', store=True)
    budgeted_hours = fields.Float(string='Total Budgeted Hours', compute='_compute_calc')
    budgeted_hours2 = fields.Float(string='Total Budgeted Hours')
    budgeted_revenue = fields.Float(string='Budgeted Revenue', digits=(16, 2))
    expenses_per = fields.Float(string='Expenses (%)', digits=(16, 2))
    expenses_amt = fields.Float(string='Expenses Amount', digits=(16, 2))
    cost = fields.Float("Total Revenue", compute='onchange_compute_values', store=True)
    consultant_cost = fields.Float("Actual Cost", compute='_compute_calc')
    actual_revenue = fields.Float("Actual Revenue", compute='_compute_calc')
    other_expenses = fields.Float(string='Other Expenses', related='expenses_amt')
    other_emp_hour = fields.Float("Other Employee Hour", compute='_compute_other_employee_cost')
    other_emp_cost = fields.Float("Other Employee Cost", compute='_compute_other_employee_cost')
    total_expenses = fields.Float(string='Total Expenses', digits=(16, 2), compute='_compute_calc', store=True)
    hourly_rate = fields.Float("Hourly Rate", default=0.0)
    hourly_rate2 = fields.Float("Hourly Ratee")
    budgeted_hour_week = fields.Float("Budgeted Hours(per week)", compute='onchange_compute_values', store=True)
    profit_amt = fields.Float(string='Profit Amount', digits=(16, 2), compute='_compute_calc', store=True)
    profit_per = fields.Float(string='Porfit Percentage', digits=(16, 2), compute='_compute_calc', store=True)
    hour_distribution = fields.Selection([
        ('Manual', 'Manual'),
        ('Percentage', 'Percentage'),
    ], string="Hours Distribution", default="Manual")
    manager_per = fields.Float(string='Manager %', digits=(16, 2))
    employee_per = fields.Float(string='Employee %', digits=(16, 2), compute='compute_percentage_hours', store=True)

    manager_hour = fields.Float(string='Manager Hour')
    employee_hour = fields.Float(string='Employee Hour')

    consultant_timesheet_hrs = fields.One2many('consultant.timesheet.hrs', 'project_id', "Timesheet Hrs",
                                               copy=False, help="Consultant timesheet hours")
    project_cons_hrs = fields.One2many('project.consultant.hrs', 'project_id', 'Consultant Allocation', copy=False)
    comment = fields.Text(string='Comment')
    tag_ids = fields.Many2many('custom.project.tags', string='Tags')
    is_check = fields.Boolean()

    @api.onchange('allowed_internal_user_ids')
    def onchange_add_allowed_internal_users(self):
        user_list = []
        consultant_list = []
        employee_obj = []
        if self.allowed_internal_user_ids:
            user_list = self.allowed_internal_user_ids.ids
        if self.sale_line_employee_ids:
            for consultant in self.sale_line_employee_ids:
                consultant_list.append(consultant.employee_id.user_id.id)
        users = (set(user_list)) - (set(consultant_list))
        for record in list(users):
            emp_obj = self.env['hr.employee'].search([('user_id', '=', record)]).id
            if emp_obj:
                employee_obj.append(emp_obj)
        if employee_obj:
            for employee in employee_obj:
                if self._origin.id:
                    if self.pricing_type == 'employee_rate':
                        self.sale_line_employee_ids.create({'project_id': self._origin.id,
                                                            'employee_id': employee})

    def _onchange_calculate_timesheet_hours(self):
        self.consultant_timesheet_hrs = [(6, 0, False)]
        if self._origin.id:
            self._cr.execute('''SELECT project_id, employee_id, SUM(unit_amount) FROM account_analytic_line
                            where project_id = %(project_id)s
                            GROUP BY project_id, employee_id''',
                             {'project_id': self._origin.id})
            res = self._cr.fetchall()
            if res:
                for rec in res:
                    self.consultant_timesheet_hrs.create({'project_id': rec[0],
                                                          'employee_id': rec[1],
                                                          'timesheet_hour': rec[2]})

    def _compute_other_employee_cost(self):
        for val in self:
            #######Code for order line consultant
            list1 = []
            if val.sale_line_employee_ids:
                for record1 in val.sale_line_employee_ids:
                    list1.append(record1.employee_id.id)
            if val._origin.id:
                self._cr.execute('''SELECT project_id, employee_id, SUM(unit_amount), SUM(amount) FROM account_analytic_line 
                where project_id = %(project_id)s
                GROUP BY project_id, employee_id''', {'project_id': val._origin.id})
                res = self._cr.fetchall()
                hour = 0.0
                cost = 0.0
                for record2 in res:
                    if record2[1] not in list1:
                        hour = hour + record2[2]
                        cost = cost + abs(record2[3])
                val.other_emp_hour = hour
                val.other_emp_cost = cost

    @api.depends('cost', 'expenses_amt', 'budgeted_revenue', 'is_check')
    def _compute_calc(self):
        for record in self:
            consultant_cost = 0.0
            actual_revenue = 0.0
            hour = 0.0
            timesheet_hour = 0.0
            for rec in record.sale_line_employee_ids:
                consultant_cost = consultant_cost + rec.consultant_cost
                actual_revenue = actual_revenue + rec.actual_revenue
                hour = hour + rec.budgeted_qty
                timesheet_hour = timesheet_hour + rec.timesheet_hour
            if record.pricing_type == 'fixed_rate':
                for rec in record.consultant_timesheet_hrs:
                    consultant_cost = consultant_cost + rec.consultant_cost
                    timesheet_hour = timesheet_hour + rec.timesheet_hour
            record.consultant_cost = consultant_cost
            if record.project_type == 'hours_in_consultant':
                record.actual_revenue = actual_revenue
            else:
                record.actual_revenue = record.hourly_rate * timesheet_hour
            record.budgeted_hours = hour
            record.timesheet_hour = timesheet_hour
            #total_exp = record.consultant_cost + record.expenses_amt
            total_exp = record.consultant_cost + record.other_emp_cost + record.expenses_amt
            record.total_expenses = total_exp
            # record.profit_amt = record.budgeted_revenue - total_exp
            # if record.profit_amt > 0 and record.budgeted_revenue > 0:
            #     record.profit_per = (record.profit_amt / record.budgeted_revenue) * 100

            ##### CR Changes
            record.profit_amt = record.actual_revenue - record.total_expenses
            if record.actual_revenue > 0.0:
                record.profit_per = (record.profit_amt / record.actual_revenue) * 100
            ########################
            if record.project_type == 'hours_in_consultant':
                if record.budgeted_hours == 0.0:
                    record.hourly_rate = 0.0
                else:
                    record.hourly_rate = (record.budgeted_revenue / record.budgeted_hours)
            ########### Added New code 14 March 2022 ###########
            if record.project_type == 'hours_no_limit':
                if record.budgeted_hours2 == 0.0:
                    record.hourly_rate = 0.0
                else:
                    record.hourly_rate = (record.budgeted_revenue / record.budgeted_hours2)
            #####################

    @api.depends('manager_per', 'hour_distribution')
    def compute_percentage_hours(self):
        for record in self:
            if record.manager_per > 100:
                raise ValidationError(_("Percentage should be less than or equal to 100"))
            if record.manager_per > 0.0:
                record.employee_per = 100 - record.manager_per
        # if self.manager_per > 100:
        #     raise ValidationError(_("Percentage should be less than or equal to 100"))

    # Validation for distributed percentage
    @api.onchange('sale_line_employee_ids')
    def onchange_validation_distribution_percentage(self):
        if self.sale_line_employee_ids:
            manager_total = 0.0
            employee_total = 0.0
            for rec in self.sale_line_employee_ids:
                if rec.distribution_per > 100:
                    raise ValidationError(_("Percentage should be less than or equal to 100"))
                """if rec.role == 'Manager':
                    manager_total = manager_total + rec.distribution_per
                    if manager_total > 100:
                        raise ValidationError(_("Manager Distribution Percentage should be less than or equal to 100"))
                else:
                    employee_total = employee_total + rec.distribution_per
                    if employee_total > 100:
                        raise ValidationError(_("Employee Distribution Percentage should be less than or equal to 100"))"""


    @api.onchange('budgeted_revenue', 'expenses_per')
    def onchange_expenses_per(self):
        if self.budgeted_revenue > 0 and self.expenses_per > 0:
            expense_amount = self.budgeted_revenue * (self.expenses_per / 100)
            self.expenses_amt = expense_amount

    @api.onchange('budgeted_revenue', 'sale_line_employee_ids', 'manager_per')
    def onchange_budgeted_hour(self):
        self.sale_line_employee_ids._compute_total_cost()
        hour = 0.0
        for rec in self.sale_line_employee_ids:
            hour = hour + rec.budgeted_qty
        self.budgeted_hours = hour
        if self.project_type == 'hours_in_consultant' and self.budgeted_hours > 0.0:
            self.hourly_rate = (self.budgeted_revenue / self.budgeted_hours)

        ########### Added New code 14 March 2022 ###########
        if self.project_type == 'hours_no_limit' and self.budgeted_hours2 > 0.0:
            self.hourly_rate = (self.budgeted_revenue / self.budgeted_hours2)
        ###############

    @api.depends('sale_line_employee_ids')
    def onchange_compute_values(self):
        for record in self:
            cost = 0.0
            budgeted_hour_week = 0.0
            for rec in record.sale_line_employee_ids:
                cost = cost + rec.cost
                budgeted_hour_week = budgeted_hour_week + rec.budgeted_hour_week
            if record.project_type == 'hours_in_consultant':
                record.cost = cost
            record.budgeted_hour_week = budgeted_hour_week

    # @api.depends('pricing_type')
    def _compute_consultant_timesheet_hour(self):
        for val in self:
            val.consultant_timesheet_hrs = False
            if val._origin.id:
                self._cr.execute('''SELECT project_id, employee_id, SUM(unit_amount) FROM account_analytic_line where project_id = 149
                GROUP BY project_id, employee_id''')
                res = self._cr.fetchone()
                if res:
                    create_timesheet = val.consultant_timesheet_hrs.create({'project_id': res[0],
                                                                            'employee_id': res[1],
                                                                            'timesheet_hour': res[2]})
                else:
                    val.consultant_timesheet_hrs = False
            # val.consultant_timesheet_hrs = False

    def action_view_custom_project_consultant_hrs_report(self):
        action = self.env["ir.actions.actions"]._for_xml_id("cor_custom.action_project_consultant_hrs_report")
        action['context'] = {'search_default_project_id': self.id, 'search_default_project': 1,
                             'search_default_consultant': 1,
                             #'search_default_sdate': 1,
                             #'search_default_group_by_hours_type': 1,
                             'default_res_model': 'project.consultant.hrs.report'}
        return action


class ProjectConsultantTimesheetHrs(models.Model):
    _name = 'consultant.timesheet.hrs'
    _description = 'Project Consultant Timesheet Hrs'

    project_id = fields.Many2one('project.project', "Project")
    employee_id = fields.Many2one('hr.employee', string="Consultant")
    employee_price = fields.Float(string="Consultant Price", default=0.0)
    timesheet_hour = fields.Float("Timesheet Hour", default=0.0)
    consultant_cost = fields.Float("Actual Cost", compute='_compute_consultant_timesheet_cost', default=0.0)

    @api.onchange('employee_id')
    def onchange_employee_price(self):
        if self.employee_id:
            self.employee_price = self.employee_id.timesheet_cost
        else:
            self.employee_price = 0.0

    def _compute_consultant_timesheet_cost(self):
        for val in self:
            # if val.employee_id.timesheet_cost:
            #     val.employee_price = val.employee_id.timesheet_cost
            # else:
            #     val.employee_price = 0.0
            if val.timesheet_hour and val.employee_id.timesheet_cost:
                val.consultant_cost = val.timesheet_hour * val.employee_id.timesheet_cost
            else:
                val.consultant_cost = 0.0


class InheritProjectProductEmployeeMap(models.Model):
    _inherit = 'project.sale.line.employee.map'

    budgeted_qty = fields.Float(string='Budgeted Hours', digits=(16, 2))
    budgeted_uom = fields.Many2one('uom.uom', string='Budgeted UOM', related='sale_line_id.product_uom', readonly=True)
    # budgeted_uom = fields.Many2one('uom.uom', string='Budgeted UOM', related='timesheet_product_id.uom_id', readonly=True)
    timesheet_hour = fields.Float("Timesheet Hour", compute='_compute_timesheet_hour', default=0.0)
    employee_price = fields.Monetary(string="Consultant Price")
    budgeted_hour_week = fields.Float("Budgeted Hours per week", compute='_compute_budgeted_hour_week')
    price_unit = fields.Float("Hourly Rate", readonly=False)
    currency_id = fields.Many2one('res.currency', string="Currency", compute='_compute_price_unit', store=True,
                                  readonly=False)
    sale_line_id = fields.Many2one('sale.order.line', "Service", domain=[('is_service', '=', True)])
    cost = fields.Float("Cost", default=0.0, store=True)
    consultant_cost = fields.Float("Actual Cost", compute='_compute_timesheet_hour', default=0.0)
    actual_revenue = fields.Float("Actual Revenue", compute='_compute_timesheet_hour', default=0.0)

    hour_distribution = fields.Selection(related='project_id.hour_distribution')
    role = fields.Selection([('Manager', 'Manager'),
                             ('Employee', 'Employee'), ], string="Role", default="Employee")
    distribution_per = fields.Float("%")
    start_date = fields.Date(string="Start Date")
    end_date = fields.Date(string="End Date")

    _sql_constraints = [
        ('uniqueness_employee', 'CHECK(1=1)', 'An employee cannot be selected more than once in the mapping. Please remove duplicate(s) and try again.'),
    ]

    @api.constrains('start_date', 'end_date')
    def _check_dates(self):
        for val in self:
            if val.start_date:
                if val.end_date:
                    if val.end_date < val.start_date:
                        raise ValidationError(_('Start date must be earlier than end date.'))
                    domain = [
                        ('start_date', '<=', val.end_date),
                        ('end_date', '>=', val.start_date),
                        ('project_id', '=', val.project_id.id),
                        ('employee_id', '=', val.employee_id.id),
                        ('id', '!=', val.id)
                    ]
                else:
                    domain = [
                        ('end_date', '>=', val.start_date),
                        ('project_id', '=', val.project_id.id),
                        ('employee_id', '=', val.employee_id.id),
                        ('id', '!=', val.id)
                    ]
                res = self.search_count(domain)
                if res > 0:
                    raise ValidationError(_('Same Consultant can not have 2 same date that overlaps on same day!'))

    @api.onchange('employee_id')
    def onchange_employee_price(self):
        if self.employee_id:
            self.employee_price = self.employee_id.timesheet_cost
        else:
            self.employee_price = 0.0

    def _compute_timesheet_hour(self):
        for val in self:
            date_clause = ""
            query_params = [val.project_id._origin.id, val.employee_id.id]
            #tz = self.env.user.tz
            if val.start_date:
                s_datetime = datetime.datetime.combine(val.start_date, datetime.time(00, 00))
                #s_datetime1 = timezone(tz).localize(s_datetime).astimezone(UTC)
                date_clause += "and start_datetime at time zone 'utc' at time zone (select tz from res_partner where id=3) >= %s"
                query_params.append(s_datetime)
            if val.end_date:
                e_datetime = datetime.datetime.combine(val.end_date, datetime.time(23, 59, 59))
                #e_datetime1 = timezone(tz).localize(e_datetime).astimezone(UTC)
                date_clause += " and end_datetime at time zone 'utc' at time zone (select tz from res_partner where id=3) <= %s"
                query_params.append(e_datetime)
            #print('1111111111111', val.project_id)
            #print('222222222222222', query_params)
            if val.project_id.is_sub_project == False:
                query = """SELECT project_id, employee_id, SUM(unit_amount) FROM account_analytic_line
                            where project_id = %s and employee_id = %s  {date_clause}
                            GROUP BY project_id, employee_id""".format(date_clause=date_clause)
            else:
                query = """SELECT sub_project, employee_id, SUM(unit_amount) FROM account_analytic_line
                                            where sub_project = %s and employee_id = %s  {date_clause}
                                            GROUP BY sub_project, employee_id""".format(date_clause=date_clause)
            self.env.cr.execute(query, query_params)
            #self._cr.execute('''SELECT project_id, employee_id, SUM(unit_amount) FROM account_analytic_line
            #where project_id = %(project_id)s and employee_id = %(employee_id)s
            #GROUP BY project_id, employee_id''',
            #                 {'project_id': val.project_id._origin.id, 'employee_id': val.employee_id.id, })
            res = self._cr.fetchone()
            if res and res[2]:
                val.timesheet_hour = res[2]
            else:
                val.timesheet_hour = 0.0
            val.consultant_cost = val.timesheet_hour * val.employee_price
            val.actual_revenue = val.timesheet_hour * val.price_unit

    def _compute_budgeted_hour_week(self):
        for val in self:
            if val.employee_id and val.employee_id.budgeted_hour_week > 0 and val.budgeted_qty:
                val.budgeted_hour_week = (val.budgeted_qty / val.employee_id.budgeted_hour_week)
            else:
                val.budgeted_hour_week = 0

    @api.onchange('project_id.budgeted_revenue', 'price_unit', 'distribution_per', 'employee_id', 'role', 'manager_per')
    def _compute_total_cost(self):
        for val in self:
            if val.project_id.project_type == 'hours_in_consultant':
                if val.hour_distribution == 'Percentage':
                    if val.role == 'Manager':
                        val.cost = val.project_id.budgeted_revenue * (val.project_id.manager_per / 100) * (
                                val.distribution_per / 100)
                    else:
                        val.cost = val.project_id.budgeted_revenue * (val.project_id.employee_per / 100) * (
                                val.distribution_per / 100)
                    if val.price_unit > 0.0:
                        val.budgeted_qty = val.cost / val.price_unit
                # val.cost = val.budgeted_qty * val.price_unit
            # val.consultant_cost = val.timesheet_hour * val.employee_price
        # self.project_id.onchange_budgeted_hour()

    @api.onchange('price_unit', 'budgeted_qty')
    def _calculate_total_cost(self):
        if self.project_id.project_type == 'hours_in_consultant':
            if self.hour_distribution == 'Manual':
                self.cost = self.price_unit * self.budgeted_qty

    @api.depends('sale_line_id', 'sale_line_id.price_unit', 'timesheet_product_id')
    def _compute_price_unit(self):
        for line in self:
            if line.sale_line_id:
                line.price_unit = line.sale_line_id.price_unit
                line.currency_id = line.sale_line_id.currency_id
            elif line.timesheet_product_id:
                line.price_unit = line.timesheet_product_id.lst_price
                line.currency_id = line.timesheet_product_id.currency_id
            else:
                # line.price_unit = 0
                line.currency_id = False

    def edit_sale_line_employee_record(self):
        context = dict(self.env.context)
        context['form_view_initial_mode'] = 'edit'
        return {
            'name': ('Edit Resource History'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new',
            'res_model': 'project.sale.line.employee.map',
            'res_id': self.id,
            'context': context,
            # 'view_id': view_id
        }

    def update_record(self):
        return {
            'type': 'ir.actions.act_window_close'}

    @api.onchange('start_date', 'end_date')
    def _onchange_start_date(self):
        # if self.start_date:
        #     self.end_date = self.start_date + relativedelta.relativedelta(months=1) - relativedelta.relativedelta(days=1)
        if self.start_date and self.end_date and (self.start_date > self.end_date):
            raise AccessError('End date could not be greater than start date')
        if self.project_id and self.project_id.date_start:
            if self.start_date and not self.project_id.date_start <= self.start_date <= self.project_id.date:
                raise AccessError(_('Allocation Start date must be between %s to %s') % (
                self.project_id.date_start.strftime('%d-%b-%Y'), self.project_id.date.strftime('%d-%b-%Y')))
            if self.end_date and not (self.project_id.date_start <= self.end_date <= self.project_id.date):
                raise AccessError(_('Allocation Start date must be between %s to %s') % (
                self.project_id.date_start.strftime('%d-%b-%Y'), self.project_id.date.strftime('%d-%b-%Y')))


class CustomProjectTags(models.Model):
    """ Tags of project's tasks """
    _name = "custom.project.tags"
    _description = "Project Tag"

    name = fields.Char('Name', required=True)

    _sql_constraints = [
        ('name_uniq', 'unique (name)', "Tag name already exists!"),
    ]


class InheritProjectTask(models.Model):
    _inherit = 'project.task'

    start_date = fields.Date(string='Start Date')

    def action_done_task(self):
        stage_id = self.env['project.task.type'].search([('is_closed', '=', True)], limit=1)
        for stage in self:
            stage.write({'stage_id': stage_id.id,
                         'active': False})
