# Copyright 2017-19 Eficent Business and IT Consulting Services S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError


class MrpProductionRequest(models.Model):
    _name = "mrp.production.request"
    _description = "Manufacturing Request"
    _inherit = "mail.thread"
    _order = "date_planned_start desc, id desc"

    @api.model
    def _company_get(self):
        company_id = self.env['res.company']._company_default_get()
        return self.env['res.company'].browse(company_id.id)

    @api.model
    def _get_default_requested_by(self):
        return self.env.user

    name = fields.Char(
        default="/", required=True,
        readonly=True, states={'draft': [('readonly', False)]})
    origin = fields.Char(
        string='Source Document',
        readonly=True, states={'draft': [('readonly', False)]})
    requested_by = fields.Many2one(
        comodel_name='res.users', string='Requested by',
        default=lambda self: self._get_default_requested_by(),
        required=True, track_visibility='onchange',
        readonly=True, states={'draft': [('readonly', False)]})
    assigned_to = fields.Many2one(
        comodel_name='res.users', string='Approver',
        track_visibility='onchange',
        readonly=True, states={'draft': [('readonly', False)]},
        domain=lambda self: [('groups_id', 'in', self.env.ref(
            'mrp_production_request.'
            'group_mrp_production_request_manager').id)])
    description = fields.Text('Description')
    date_planned_start = fields.Datetime(
        'Deadline Start', copy=False, default=fields.Datetime.now,
        index=True, required=True,
        states={'confirmed': [('readonly', False)]}, oldname="date_planned")
    date_planned_finished = fields.Datetime(
        'Deadline End', copy=False, default=fields.Datetime.now,
        index=True,
        states={'confirmed': [('readonly', False)]})
    company_id = fields.Many2one(
        comodel_name='res.company', string='Company',
        required=True, default=lambda self: self._company_get())
    mrp_production_ids = fields.One2many(
        comodel_name="mrp.production", string="Manufacturing Orders",
        inverse_name="mrp_production_request_id", readonly=True)
    mrp_production_count = fields.Integer(
        compute="_compute_mrp_production_count",
        string="MO's Count",
    )
    state = fields.Selection(
        selection=[("draft", "Draft"),
                   ("to_approve", "To Be Approved"),
                   ("approved", "Approved"),
                   ("done", "Done"),
                   ("cancel", "Cancelled")],
        index=True, track_visibility='onchange',
        required=True, copy=False, default='draft')
    procurement_group_id = fields.Many2one(
        string='Procurement Group',
        comodel_name='procurement.group',
        copy=False)
    propagate = fields.Boolean(
        'Propagate cancel and split',
        help='If checked, when the previous move of the move '
             '(which was generated by a next procurement) is cancelled '
             'or split, the move generated by this move will too')
    product_id = fields.Many2one(
        comodel_name="product.product", string="Product", required=True,
        domain=[('type', 'in', ['product', 'consu'])],
        track_visibility="onchange",
        readonly=True, states={'draft': [('readonly', False)]})
    product_tmpl_id = fields.Many2one(
        comodel_name='product.template', string='Product Template',
        related='product_id.product_tmpl_id')
    product_qty = fields.Float(
        string="Required Quantity", required=True, track_visibility='onchange',
        digits=dp.get_precision('Product Unit of Measure'), default=1.0,
        readonly=True, states={'draft': [('readonly', False)]})
    product_uom_id = fields.Many2one(
        comodel_name='uom.uom', string='Unit of Measure',
        readonly=True, states={'draft': [('readonly', False)]},
        domain="[('category_id', '=', category_uom_id)]")
    category_uom_id = fields.Many2one(related="product_uom_id.category_id")
    manufactured_qty = fields.Float(
        string="Quantity in Manufacturing Orders",
        compute="_compute_manufactured_qty", store=True, readonly=True,
        digits=dp.get_precision('Product Unit of Measure'),
        help="Sum of the quantities in Manufacturing Orders (in any state).")
    done_qty = fields.Float(
        string="Quantity Done", store=True, readonly=True,
        compute="_compute_manufactured_qty",
        digits=dp.get_precision('Product Unit of Measure'),
        help="Sum of the quantities in all done Manufacturing Orders.")
    pending_qty = fields.Float(
        string="Pending Quantity", compute="_compute_manufactured_qty",
        store=True, digits=dp.get_precision('Product Unit of Measure'),
        readonly=True,
        help="Quantity pending to add to Manufacturing Orders "
             "to fulfill the Manufacturing Request requirement.")
    bom_id = fields.Many2one(
        comodel_name="mrp.bom", string="Bill of Materials", required=True,
        readonly=True, states={'draft': [('readonly', False)]})
    routing_id = fields.Many2one(
        comodel_name='mrp.routing', string='Routing',
        on_delete='setnull', readonly=True,
        states={'draft': [('readonly', False)]},
        help="The list of operations (list of work centers) to produce "
             "the finished product. The routing is mainly used to compute "
             "work center costs during operations and to plan future loads "
             "on work centers based on production plannification.")
    location_src_id = fields.Many2one(
        comodel_name='stock.location', string='Raw Materials Location',
        default=lambda self: self.env['stock.location'].browse(
            self.env['mrp.production']._get_default_location_src_id()),
        required=True, readonly=True, states={'draft': [('readonly', False)]})
    location_dest_id = fields.Many2one(
        comodel_name='stock.location', string='Finished Products Location',
        default=lambda self: self.env['stock.location'].browse(
            self.env['mrp.production']._get_default_location_dest_id()),
        required=True, readonly=True, states={'draft': [('readonly', False)]})
    picking_type_id = fields.Many2one(
        comodel_name='stock.picking.type', string='Picking Type',
        default=lambda self: self.env['stock.picking.type'].browse(
            self.env['mrp.production']._get_default_picking_type()),
        required=True, readonly=True, states={'draft': [('readonly', False)]})
    move_dest_ids = fields.One2many(
        comodel_name='stock.move',
        inverse_name='created_mrp_production_request_id',
        string="Stock Movements of Produced Goods")
    orderpoint_id = fields.Many2one(
        comodel_name='stock.warehouse.orderpoint',
        string='Orderpoint')

    _sql_constraints = [
        ('name_uniq', 'unique(name, company_id)',
         'Reference must be unique per Company!'),
    ]

    @api.model
    def _get_mo_valid_states(self):
        return ['planned', 'confirmed', 'progress', 'done']

    @api.multi
    @api.depends('mrp_production_ids', 'mrp_production_ids.state', 'state')
    def _compute_manufactured_qty(self):
        valid_states = self._get_mo_valid_states()
        for req in self:
            done_mo = req.mrp_production_ids.filtered(
                lambda mo: mo.state in 'done').mapped('product_qty')
            req.done_qty = sum(done_mo)
            valid_mo = req.mrp_production_ids.filtered(
                lambda mo: mo.state in valid_states).mapped('product_qty')
            req.manufactured_qty = sum(valid_mo)
            req.pending_qty = max(req.product_qty - req.manufactured_qty, 0.0)

    @api.multi
    def _compute_mrp_production_count(self):
        for rec in self:
            rec.mrp_production_count = len(rec.mrp_production_ids)

    @api.onchange('product_id')
    def _onchange_product_id(self):
        self.product_uom_id = self.product_id.uom_id
        self.bom_id = self.env['mrp.bom']._bom_find(
            product=self.product_id, company_id=self.company_id.id,
            picking_type=self.picking_type_id)

    @api.multi
    def _subscribe_assigned_user(self, vals):
        self.ensure_one()
        if vals.get('assigned_to'):
            self.message_subscribe(
                partner_ids=self.assigned_to.mapped('partner_id').ids)

    @api.model
    def _create_sequence(self, vals):
        if not vals.get('name') or vals.get('name') == '/':
            vals['name'] = self.env['ir.sequence'].next_by_code(
                'mrp.production.request') or '/'
        return vals

    @api.model
    def create(self, vals):
        """Add sequence if name is not defined and subscribe to the thread
        the user assigned to the request."""
        vals = self._create_sequence(vals)
        res = super().create(vals)
        res._subscribe_assigned_user(vals)
        return res

    @api.multi
    def write(self, vals):
        res = super().write(vals)
        for request in self:
            request._subscribe_assigned_user(vals)
        return res

    @api.multi
    def button_to_approve(self):
        self.write({'state': 'to_approve'})
        return True

    @api.multi
    def button_approved(self):
        self.write({'state': 'approved'})
        return True

    @api.multi
    def button_done(self):
        self.write({'state': 'done'})
        return True

    @api.multi
    def _check_reset_allowed(self):
        if any([s in self._get_mo_valid_states() for s in self.mapped(
                'mrp_production_ids.state')]):
            raise UserError(
                _("You cannot reset a manufacturing request if the related "
                  "manufacturing orders are not cancelled."))

    @api.multi
    def button_draft(self):
        self._check_reset_allowed()
        self.write({'state': 'draft'})
        return True

    @api.multi
    def _check_cancel_allowed(self):
        if any([s == 'done' for s in self.mapped('state')]):
            raise UserError(
                _('You cannot reject a manufacturing request related to '
                  'done procurement orders.'))

    @api.multi
    def button_cancel(self):
        self._check_cancel_allowed()
        self.write({'state': 'cancel'})
        self.mapped('move_dest_ids').filtered(
            lambda r: r.state != 'cancel')._action_cancel()
        return True

    @api.multi
    def action_view_mrp_productions(self):
        action = self.env.ref('mrp.mrp_production_action')
        result = action.read()[0]
        result['context'] = {}
        mos = self.mapped('mrp_production_ids')
        # choose the view_mode accordingly
        if len(mos) != 1:
            result['domain'] = [('id', 'in', mos.ids)]
        elif len(mos) == 1:
            form = self.env.ref('mrp.mrp_production_form_view', False)
            result['views'] = [(form and form.id or False, 'form')]
            result['res_id'] = mos[0].id
        return result
