# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
# Copyright 2019 Odoo
# Copyright 2020 Tecnativa - Alexandre Díaz
# Copyright 2020 Tecnativa - Pedro M. Baeza

from datetime import timedelta

from odoo import api, fields, models
from odoo.tools import float_is_zero


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

    display_action_record_components = fields.Boolean(
        compute='_compute_display_action_record_components')

    @api.depends('state')
    def _compute_display_action_record_components(self):
        for picking in self:
            # Hide if not encoding state
            if picking.state in ('draft', 'cancel', 'done'):
                picking.display_action_record_components = False
                continue
            if not picking._is_subcontract():
                picking.display_action_record_components = False
                continue
            # Hide if no components are track
            subcontracted_productions = picking\
                ._get_subcontracted_productions()
            subcontracted_moves = subcontracted_productions.mapped(
                'move_raw_ids')
            if all(subcontracted_move.has_tracking == 'none'
                   for subcontracted_move in subcontracted_moves):
                picking.display_action_record_components = False
                continue
            # Hide if the production is to close
            if not subcontracted_productions.filtered(lambda mo: (
                not mo.check_to_done and mo.state != 'done'
            )):
                picking.display_action_record_components = False
                continue
            picking.display_action_record_components = True

    # -------------------------------------------------------------------------
    # Action methods
    # -------------------------------------------------------------------------

    def action_done(self):
        productions = self.env['mrp.production']
        for picking in self:
            for move in picking.move_lines:
                production = move.move_orig_ids.mapped('production_id')
                if not move.is_subcontract or production.state in ('done', 'cancel'):
                    continue
                if move._has_tracked_subcontract_components():
                    move.move_orig_ids.filtered(
                        lambda m: m.state not in ('done', 'cancel')
                    ).move_line_ids.unlink()
                    move_finished_ids = move.move_orig_ids.filtered(
                        lambda m: m.state not in ('done', 'cancel'))
                    for ml in move.move_line_ids:
                        if float_is_zero(
                            ml.qty_done,
                            precision_rounding=ml.product_uom_id.rounding
                        ):
                            continue
                        ml.copy({
                            'picking_id': False,
                            'production_id':
                                move_finished_ids.production_id.id,
                            'move_id': move_finished_ids.id,
                            'qty_done': ml.qty_done,
                            'result_package_id': False,
                            'location_id': move_finished_ids.location_id.id,
                            'location_dest_id':
                                move_finished_ids.location_dest_id.id,
                        })
                else:
                    for move_line in move.move_line_ids:
                        if float_is_zero(
                            move_line.qty_done,
                            precision_rounding=move_line.product_uom_id.rounding
                        ):
                            continue
                        produce = self.env['mrp.product.produce'].with_context(
                            default_production_id=production.id,
                            active_id=production.id).create({
                                'production_id': production.id,
                                'product_id': production.product_id.id,
                                'product_qty': move_line.qty_done,
                                'product_uom_id': move_line.product_uom_id.id,
                                'lot_id': move_line.lot_id.id,
                            })
                        produce._onchange_product_qty()
                        produce.do_produce()
                productions |= production
        for subcontracted_production in productions:
            if subcontracted_production.check_to_done:
                subcontracted_production.button_mark_done()
            else:
                subcontracted_production.post_inventory()
        res = super(StockPicking, self).action_done()
        for subcontracted_production in productions:
            # For consistency, set the date on production move before the
            # date on picking. (Traceability report + Product Moves menu
            # item)
            minimum_date = min(picking.move_line_ids.mapped('date'))
            production_moves = subcontracted_production.move_raw_ids\
                | subcontracted_production.move_finished_ids
            production_moves.write({
                'date': minimum_date - timedelta(seconds=1),
            })
            production_moves.mapped('move_line_ids').write({
                'date': minimum_date - timedelta(seconds=1),
            })
        return res

    def action_record_components(self):
        self.ensure_one()
        for move in self.move_lines:
            if not move._has_tracked_subcontract_components():
                continue
            production = move.move_orig_ids.mapped("production_id")
            if not production or production.state in ('done', 'to_close'):
                continue
            return move._action_record_components()

    # -------------------------------------------------------------------------
    # Subcontract helpers
    # -------------------------------------------------------------------------
    def _is_subcontract(self):
        self.ensure_one()
        return self.picking_type_id.code == 'incoming' and any(
            m.is_subcontract for m in self.move_lines)

    def _get_subcontracted_productions(self):
        self.ensure_one()
        return self.move_lines.mapped('move_orig_ids.production_id')

    def _get_warehouse(self, subcontract_move):
        return subcontract_move.warehouse_id\
            or self.picking_type_id.warehouse_id

    def _prepare_subcontract_mo_vals(self, subcontract_move, bom):
        subcontract_move.ensure_one()
        group = self.env['procurement.group'].create({
            'name': self.name,
            'partner_id': self.partner_id.id,
        })
        product = subcontract_move.product_id
        warehouse = self._get_warehouse(subcontract_move)
        vals = {
            'company_id': subcontract_move.company_id.id,
            'procurement_group_id': group.id,
            'product_id': product.id,
            'product_uom_id': subcontract_move.product_uom.id,
            'bom_id': bom.id,
            'location_src_id':
                subcontract_move.picking_id.partner_id.with_context(
                    force_company=subcontract_move.company_id.id)
                .property_stock_subcontractor.id,
            'location_dest_id':
                subcontract_move.picking_id.partner_id.with_context(
                    force_company=subcontract_move.company_id.id)
                .property_stock_subcontractor.id,
            'product_qty': subcontract_move.product_uom_qty,
            'picking_type_id': warehouse.subcontracting_type_id.id
        }
        return vals

    def _subcontracted_produce(self, subcontract_details):
        self.ensure_one()
        for move, bom in subcontract_details:
            mo = self.env['mrp.production'].with_context(
                force_company=move.company_id.id
            ).create(self._prepare_subcontract_mo_vals(move, bom))
            # Link the finished to the receipt move.
            finished_move = mo.move_finished_ids.filtered(
                lambda m: m.product_id == move.product_id)
            finished_move.write({'move_dest_ids': [(4, move.id, False)]})
            mo.action_assign()
