# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import time

from odoo import _, api, fields, models
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools.misc import clean_context


class PickingType(models.Model):
    _inherit = 'stock.picking.type'

    code = fields.Selection(selection_add=[
        ('repair_operation', 'Repair')
    ], ondelete={'repair_operation': 'cascade'})

    count_repair_confirmed = fields.Integer(
        string="Number of Repair Orders Confirmed", compute='_compute_count_repair')
    count_repair_under_repair = fields.Integer(
        string="Number of Repair Orders Under Repair", compute='_compute_count_repair')
    count_repair_ready = fields.Integer(
        string="Number of Repair Orders to Process", compute='_compute_count_repair')
    count_repair_late = fields.Integer(
        string="Number of Late Repair Orders", compute='_compute_count_repair')

    default_product_location_src_id = fields.Many2one(
        'stock.location', 'Product Source Location', compute='_compute_default_product_location_id',
        check_company=True, store=True, readonly=False, precompute=True,
        help="This is the default source location for the product to be repaired in repair orders with this operation type.")
    default_product_location_dest_id = fields.Many2one(
        'stock.location', 'Product Destination Location', compute='_compute_default_product_location_id',
        check_company=True, store=True, readonly=False, precompute=True,
        help="This is the default destination location for the product to be repaired in repair orders with this operation type.")
    default_remove_location_dest_id = fields.Many2one(
        'stock.location', 'Remove Destination Location', compute='_compute_default_remove_location_dest_id',
        check_company=True, store=True, readonly=False, precompute=True,
        help="This is the default remove destination location when you create a repair order with this operation type.")
    default_recycle_location_dest_id = fields.Many2one(
        'stock.location', 'Recycle Destination Location', compute='_compute_default_recycle_location_dest_id',
        check_company=True, store=True, readonly=False, precompute=True,
        help="This is the default recycle destination location when you create a repair order with this operation type.")

    is_repairable = fields.Boolean(
        'Create Repair Orders from Returns',
        compute='_compute_is_repairable', store=True, readonly=False, default=False,
        help="If ticked, you will be able to directly create repair orders from a return.")
    return_type_of_ids = fields.One2many('stock.picking.type', 'return_picking_type_id')
    repair_properties_definition = fields.PropertiesDefinition('Repair Properties')

    def _compute_count_repair(self):
        repair_picking_types = self.filtered(lambda picking: picking.code == 'repair_operation')

        # By default, set count_repair_xxx to False
        self.count_repair_ready = False
        self.count_repair_confirmed = False
        self.count_repair_under_repair = False
        self.count_repair_late = False

        # shortcut
        if not repair_picking_types:
            return

        picking_types = self.env['repair.order']._read_group(
            [
                ('picking_type_id', 'in', repair_picking_types.ids),
                ('state', 'in', ('confirmed', 'under_repair')),
            ],
            groupby=['picking_type_id', 'is_parts_available', 'state'],
            aggregates=['id:count']
        )

        late_repairs = self.env['repair.order']._read_group(
            [
                ('picking_type_id', 'in', repair_picking_types.ids),
                ('state', '=', 'confirmed'),
                '|',
                ('schedule_date', '<', fields.Date.today()),
                ('is_parts_late', '=', True),
            ],
            groupby=['picking_type_id'],
            aggregates=['__count']
        )
        late_repairs = {pt.id: late_count for pt, late_count in late_repairs}

        counts = {}
        for pt in picking_types:
            pt_count = counts.setdefault(pt[0].id, {})
            # Only confirmed repairs (not "under repair" ones) are considered as ready
            if pt[1] and pt[2] == 'confirmed':
                pt_count.setdefault('ready', 0)
                pt_count['ready'] += pt[3]
            pt_count.setdefault(pt[2], 0)
            pt_count[pt[2]] += pt[3]

        for pt in repair_picking_types:
            if pt.id not in counts:
                continue
            pt.count_repair_ready = counts[pt.id].get('ready')
            pt.count_repair_confirmed = counts[pt.id].get('confirmed')
            pt.count_repair_under_repair = counts[pt.id].get('under_repair')
            pt.count_repair_late = late_repairs.get(pt.id, 0)

    @api.depends('return_type_of_ids', 'code')
    def _compute_is_repairable(self):
        for picking_type in self:
            if not picking_type.return_type_of_ids:
                picking_type.is_repairable = False  # Reset the user choice as it's no more available.

    def _compute_default_location_src_id(self):
        remaining_picking_type = self.env['stock.picking.type']
        for picking_type in self:
            if picking_type.code != 'repair_operation':
                remaining_picking_type |= picking_type
                continue
            stock_location = picking_type.warehouse_id.lot_stock_id
            picking_type.default_location_src_id = stock_location.id
        super(PickingType, remaining_picking_type)._compute_default_location_src_id()

    def _compute_default_location_dest_id(self):
        repair_picking_type = self.filtered(lambda pt: pt.code == 'repair_operation')
        prod_locations = self.env['stock.location']._read_group(
            [('usage', '=', 'production'), ('company_id', 'in', repair_picking_type.company_id.ids)],
            ['company_id'],
            ['id:min'],
        )
        prod_locations = {l[0].id: l[1] for l in prod_locations}
        for picking_type in repair_picking_type:
            picking_type.default_location_dest_id = prod_locations.get(picking_type.company_id.id)
        super(PickingType, (self - repair_picking_type))._compute_default_location_dest_id()

    @api.depends('code')
    def _compute_default_product_location_id(self):
        for picking_type in self:
            if picking_type.code == 'repair_operation':
                stock_location = picking_type.warehouse_id.lot_stock_id
                picking_type.default_product_location_src_id = stock_location.id
                picking_type.default_product_location_dest_id = stock_location.id

    @api.depends('code')
    def _compute_default_remove_location_dest_id(self):
        repair_picking_type = self.filtered(lambda pt: pt.code == 'repair_operation')
        company_ids = repair_picking_type.company_id.ids
        company_ids.append(False)
        scrap_locations = self.env['stock.location']._read_group(
            [('scrap_location', '=', True), ('company_id', 'in', company_ids)],
            ['company_id'],
            ['id:min'],
        )
        scrap_locations = {l[0].id: l[1] for l in scrap_locations}
        for picking_type in repair_picking_type:
            picking_type.default_remove_location_dest_id = scrap_locations.get(picking_type.company_id.id)

    @api.depends('code')
    def _compute_default_recycle_location_dest_id(self):
        for picking_type in self:
            if picking_type.code == 'repair_operation':
                stock_location = picking_type.warehouse_id.lot_stock_id
                picking_type.default_recycle_location_dest_id = stock_location.id

    def get_repair_stock_picking_action_picking_type(self):
        action = self.env["ir.actions.actions"]._for_xml_id('repair.action_picking_repair')
        if self:
            action['display_name'] = self.display_name
        return action

    def _get_aggregated_records_by_date(self):
        repair_picking_types = self.filtered(lambda picking: picking.code == 'repair_operation')
        other_picking_types = (self - repair_picking_types)

        records = super(PickingType, other_picking_types)._get_aggregated_records_by_date()
        repair_records = self.env['repair.order']._read_group(
            [
                ('picking_type_id', 'in', repair_picking_types.ids),
                ('state', '=', 'confirmed')
            ],
            ['picking_type_id'],
            ['schedule_date' + ':array_agg'],
        )
        # Make sure that all picking type IDs are represented, even if empty
        picking_type_id_to_dates = {i: [] for i in repair_picking_types.ids}
        picking_type_id_to_dates.update({r[0].id: r[1] for r in repair_records})
        repair_records = [(i, d, _('Confirmed')) for i, d in picking_type_id_to_dates.items()]
        return records + repair_records

    def action_repair_overview(self):
        routing_count = self.env['stock.picking.type'].search_count([('code', '=', 'repair_operation')])
        if routing_count == 1:
            return self.env['ir.actions.actions']._for_xml_id('repair.action_repair_order_tree')
        return self.env['ir.actions.actions']._for_xml_id('repair.action_repair_picking_type_kanban')


class Picking(models.Model):
    _inherit = 'stock.picking'

    is_repairable = fields.Boolean(compute='_compute_is_repairable')
    repair_ids = fields.One2many('repair.order', 'picking_id')
    nbr_repairs = fields.Integer('Number of repairs linked to this picking', compute='_compute_nbr_repairs')

    @api.depends('picking_type_id.is_repairable', 'return_id')
    def _compute_is_repairable(self):
        for picking in self:
            picking.is_repairable = picking.picking_type_id.is_repairable and picking.return_id

    @api.depends('repair_ids')
    def _compute_nbr_repairs(self):
        for picking in self:
            picking.nbr_repairs = len(picking.repair_ids)

    def action_repair_return(self):
        self.ensure_one()
        ctx = clean_context(self.env.context.copy())
        ctx.update({
            'default_product_location_src_id': self.location_dest_id.id,
            'default_repair_picking_id': self.id,
            'default_picking_type_id': self.picking_type_id.warehouse_id.repair_type_id.id,
            'default_partner_id': self.partner_id and self.partner_id.id or False,
        })
        return {
            'name': _('Create Repair'),
            'type': 'ir.actions.act_window',
            'view_mode': 'form',
            'res_model': 'repair.order',
            'view_id': self.env.ref('repair.view_repair_order_form').id,
            'context': ctx,
        }

    def action_view_repairs(self):
        if self.repair_ids:
            action = {
                'res_model': 'repair.order',
                'type': 'ir.actions.act_window',
            }
            if len(self.repair_ids) == 1:
                action.update({
                    'view_mode': 'form',
                    'res_id': self.repair_ids[0].id,
                })
            else:
                action.update({
                    'name': _('Repair Orders'),
                    'view_mode': 'list,form',
                    'domain': [('id', 'in', self.repair_ids.ids)],
                })
            return action

    @api.model
    def get_action_click_graph(self):
        picking_type_code = self.env["stock.picking.type"].browse(
            self.env.context["picking_type_id"]
        ).code

        if picking_type_code == "repair_operation":
            action = self._get_action("repair.action_picking_repair_graph")
            if self:
                action["context"].update({
                    "default_picking_type_id": self.picking_type_id,
                    "picking_type_id": self.picking_type_id,
                })
            return action

        return super().get_action_click_graph()
