# Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError, AccessError


class StockRequestOrder(models.Model):
    _name = 'stock.request.order'
    _description = 'Stock Request Order'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _order = 'id desc'

    @api.model
    def default_get(self, fields):
        res = super().default_get(fields)
        warehouse = None
        if 'warehouse_id' not in res and res.get('company_id'):
            warehouse = self.env['stock.warehouse'].search(
                [('company_id', '=', res['company_id'])], limit=1)
        if warehouse:
            res['warehouse_id'] = warehouse.id
            res['location_id'] = warehouse.lot_stock_id.id
        return res

    def __get_request_order_states(self):
        return self.env['stock.request']._get_request_states()

    def _get_request_order_states(self):
        return self.__get_request_order_states()

    def _get_default_requested_by(self):
        return self.env['res.users'].browse(self.env.uid)

    name = fields.Char(
        'Name', copy=False, required=True, readonly=True,
        states={'draft': [('readonly', False)]},
        default='/')
    state = fields.Selection(
        selection=_get_request_order_states,
        string='Status', copy=False, default='draft', index=True,
        readonly=True, track_visibility='onchange',
    )
    requested_by = fields.Many2one(
        'res.users', 'Requested by', required=True,
        track_visibility='onchange',
        default=lambda s: s._get_default_requested_by(),
    )
    warehouse_id = fields.Many2one(
        'stock.warehouse', 'Warehouse', readonly=True,
        ondelete="cascade", required=True,
        states={'draft': [('readonly', False)]})
    location_id = fields.Many2one(
        'stock.location', 'Location', readonly=True,
        domain=[('usage', 'in', ['internal', 'transit'])],
        ondelete="cascade", required=True,
        states={'draft': [('readonly', False)]},
    )
    allow_virtual_location = fields.Boolean(
        related='company_id.stock_request_allow_virtual_loc',
        readonly=True,
    )
    procurement_group_id = fields.Many2one(
        'procurement.group', 'Procurement Group', readonly=True,
        states={'draft': [('readonly', False)]},
        help="Moves created through this stock request will be put in this "
             "procurement group. If none is given, the moves generated by "
             "procurement rules will be grouped into one big picking.",
    )
    company_id = fields.Many2one(
        'res.company', 'Company', required=True, readonly=True,
        states={'draft': [('readonly', False)]},
        default=lambda self: self.env['res.company']._company_default_get(
            'stock.request.order'),
    )
    expected_date = fields.Datetime(
        'Expected Date', default=fields.Datetime.now, index=True,
        required=True, readonly=True,
        states={'draft': [('readonly', False)]},
        help="Date when you expect to receive the goods.",
    )
    picking_policy = fields.Selection([
        ('direct', 'Receive each product when available'),
        ('one', 'Receive all products at once')],
        string='Shipping Policy', required=True, readonly=True,
        states={'draft': [('readonly', False)]},
        default='direct',
    )
    move_ids = fields.One2many(comodel_name='stock.move',
                               compute='_compute_move_ids',
                               string='Stock Moves', readonly=True,
                               )
    picking_ids = fields.One2many('stock.picking',
                                  compute='_compute_picking_ids',
                                  string='Pickings', readonly=True,
                                  )
    picking_count = fields.Integer(string='Delivery Orders',
                                   compute='_compute_picking_ids',
                                   readonly=True,
                                   )
    stock_request_ids = fields.One2many(
        'stock.request',
        inverse_name='order_id',
        copy=True,
    )
    stock_request_count = fields.Integer(
        string='Stock requests',
        compute='_compute_stock_request_count',
        readonly=True,
    )

    _sql_constraints = [
        ('name_uniq', 'unique(name, company_id)',
         'Stock Request name must be unique'),
    ]

    @api.depends('stock_request_ids.allocation_ids')
    def _compute_picking_ids(self):
        for record in self:
            record.picking_ids = record.stock_request_ids.mapped('picking_ids')
            record.picking_count = len(record.picking_ids)

    @api.depends('stock_request_ids')
    def _compute_move_ids(self):
        for record in self:
            record.move_ids = record.stock_request_ids.mapped('move_ids')

    @api.depends('stock_request_ids')
    def _compute_stock_request_count(self):
        for record in self:
            record.stock_request_count = len(record.stock_request_ids)

    @api.onchange('requested_by')
    def onchange_requested_by(self):
        self.change_childs()

    @api.onchange('expected_date')
    def onchange_expected_date(self):
        self.change_childs()

    @api.onchange('picking_policy')
    def onchange_picking_policy(self):
        self.change_childs()

    @api.onchange('location_id')
    def onchange_location_id(self):
        if self.location_id:
            loc_wh = self.location_id.sudo().get_warehouse()
            if loc_wh and self.warehouse_id != loc_wh:
                self.warehouse_id = loc_wh
                self.with_context(
                    no_change_childs=True).onchange_warehouse_id()
        self.change_childs()

    @api.onchange('allow_virtual_location')
    def onchange_allow_virtual_location(self):
        if self.allow_virtual_location:
            return {'domain': {'location_id': []}}

    @api.onchange('warehouse_id')
    def onchange_warehouse_id(self):
        if self.warehouse_id:
            # search with sudo because the user may not have permissions
            loc_wh = self.location_id.sudo().get_warehouse()
            if self.warehouse_id != loc_wh:
                self.location_id = self.warehouse_id.sudo().lot_stock_id
                self.with_context(no_change_childs=True).onchange_location_id()
            if self.warehouse_id.sudo().company_id != self.company_id:
                self.company_id = self.warehouse_id.company_id
                self.with_context(no_change_childs=True).onchange_company_id()
        self.change_childs()

    @api.onchange('procurement_group_id')
    def onchange_procurement_group_id(self):
        self.change_childs()

    @api.onchange('company_id')
    def onchange_company_id(self):
        if self.company_id and (
            not self.warehouse_id or
            self.warehouse_id.sudo().company_id != self.company_id
        ):
            self.warehouse_id = self.env['stock.warehouse'].search(
                [('company_id', '=', self.company_id.id)], limit=1)
            self.with_context(no_change_childs=True).onchange_warehouse_id()
        self.change_childs()
        return {
            'domain': {
                'warehouse_id': [('company_id', '=', self.company_id.id)]}}

    def change_childs(self):
        if not self._context.get('no_change_childs', False):
            for line in self.stock_request_ids:
                line.warehouse_id = self.warehouse_id
                line.location_id = self.location_id
                line.company_id = self.company_id
                line.picking_policy = self.picking_policy
                line.expected_date = self.expected_date
                line.requested_by = self.requested_by
                line.procurement_group_id = self.procurement_group_id

    @api.multi
    def action_confirm(self):
        self.mapped('stock_request_ids').action_confirm()
        self.write({'state': 'open'})
        return True

    def action_draft(self):
        self.mapped('stock_request_ids').action_draft()
        self.write({'state': 'draft'})
        return True

    def action_cancel(self):
        self.mapped('stock_request_ids').action_cancel()
        self.write({'state': 'cancel'})
        return True

    def action_done(self):
        self.write({'state': 'done'})
        return True

    def check_done(self):
        for rec in self:
            if not rec.stock_request_ids.filtered(lambda r: r.state != 'done'):
                rec.action_done()
        return

    @api.multi
    def action_view_transfer(self):
        action = self.env.ref('stock.action_picking_tree_all').read()[0]

        pickings = self.mapped('picking_ids')
        if len(pickings) > 1:
            action['domain'] = [('id', 'in', pickings.ids)]
        elif pickings:
            action['views'] = [
                (self.env.ref('stock.view_picking_form').id, 'form')]
            action['res_id'] = pickings.id
        return action

    @api.multi
    def action_view_stock_requests(self):
        action = self.env.ref(
            'stock_request.action_stock_request_form').read()[0]
        if len(self.stock_request_ids) > 1:
            action['domain'] = [('order_id', 'in', self.ids)]
        elif self.stock_request_ids:
            action['views'] = [
                (self.env.ref(
                    'stock_request.view_stock_request_form').id, 'form')]
            action['res_id'] = self.stock_request_ids.id
        return action

    @api.model
    def create(self, vals):
        upd_vals = vals.copy()
        if upd_vals.get('name', '/') == '/':
            upd_vals['name'] = self.env['ir.sequence'].next_by_code(
                'stock.request.order')
        return super().create(upd_vals)

    @api.multi
    def unlink(self):
        if self.filtered(lambda r: r.state != 'draft'):
            raise UserError(_('Only orders on draft state can be unlinked'))
        return super().unlink()

    @api.constrains('warehouse_id', 'company_id')
    def _check_warehouse_company(self):
        if any(request.warehouse_id.company_id !=
                request.company_id for request in self):
            raise ValidationError(
                _('The company of the stock request must match with '
                  'that of the warehouse.'))

    @api.constrains('location_id', 'company_id')
    def _check_location_company(self):
        if any(request.location_id.company_id and
               request.location_id.company_id !=
               request.company_id for request in self):
            raise ValidationError(
                _('The company of the stock request must match with '
                  'that of the location.'))

    @api.model
    def _create_from_product_multiselect(self, products):
        if not products:
            return False
        if products._name not in ('product.product', 'product.template'):
            raise ValidationError(
                _("This action only works in the context of products"))
        if products._name == 'product.template':
            # search instead of mapped so we don't include archived variants
            products = self.env['product.product'].search([
                ('product_tmpl_id', 'in', products.ids)
            ])
        expected = self.default_get(['expected_date'])['expected_date']
        try:
            order = self.env['stock.request.order'].create(dict(
                expected_date=expected,
                stock_request_ids=[(0, 0, dict(
                    product_id=product.id,
                    product_uom_id=product.uom_id.id,
                    product_uom_qty=1.0,
                    expected_date=expected,
                )) for product in products]
            ))
        except AccessError:
            # TODO: if there is a nice way to hide the action from the
            # Action-menu if the user doesn't have the necessary rights,
            # that would be a better way of doing this
            raise UserError(_(
                "Unfortunately it seems you do not have the necessary rights "
                "for creating stock requests. Please contact your "
                "administrator."))
        action = self.env.ref('stock_request.stock_request_order_action'
                              ).read()[0]
        action['views'] = [(
            self.env.ref('stock_request.stock_request_order_form').id, 'form')]
        action['res_id'] = order.id
        return action
