# Copyright 2020 VentorTech OU
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0).

from odoo import api, models, fields
from odoo.tools.float_utils import float_compare

import functools


class StockQuant(models.Model):
    _inherit = 'stock.quant'

    removal_prio = fields.Integer(
        related="location_id.removal_prio",
        store=True,
    )

    @api.model
    def _get_removal_strategy_order(self, removal_strategy):
        # THIS IS A OVERRIDE STANDARD METHOD
        strategy_order = self.env.user.company_id.outgoing_routing_order

        if removal_strategy == 'location_priority':
            return 'removal_prio %s, id' % (['ASC', 'DESC'][int(strategy_order)])
        return super(StockQuant, self)._get_removal_strategy_order(removal_strategy)

    @api.model
    def _update_reserved_quantity(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None, strict=False):
        """ Updates reserved quantity in quants
        """
        self = self.with_context(reservation_strategy=self.env.user.company_id.stock_reservation_strategy, reservation_quantity=quantity)
        return super(StockQuant, self)._update_reserved_quantity(product_id, location_id, quantity, lot_id, package_id, owner_id, strict)

    def _gather(self, product_id, location_id, lot_id=None, package_id=None, owner_id=None, strict=False):
        """ Gather (and reorder, if required) quants
        """
        context = dict(self.env.context)
        quants = super(StockQuant, self)._gather(product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict)

        if self._context.get('skip_ventor_reordering') or (product_id.categ_id and product_id.categ_id.removal_strategy_id)\
                or location_id.removal_strategy_id:
            return quants

        strategy = self.env.user.company_id.stock_reservation_strategy

        func_reorder = getattr(self, '_reorder_{}'.format(strategy))
        if func_reorder:
            quants = func_reorder(quants, product_id)

        # mom taught us to clean after ourselves
        if context.get('reservation_strategy'):
            del context['reservation_strategy']
        if context.get('reservation_quantity'):
            del context['reservation_quantity']
        self = self.with_context(context)

        return quants

    def _reorder_none(self, quants, product_id):
        """ No reorder, i.e. use out of the box strategy
        """
        return quants

    def _reorder_base(self, quants, product_id):
        """ Reorders quants by location removal priority
        """
        def _r_getattr(obj, attr, *args):
            return functools.reduce(getattr, [obj] + attr.split('.'))

        route = self.env.user.company_id.outgoing_routing_strategy
        order = self.env.user.company_id.outgoing_routing_order

        return quants.sorted(
            key=lambda op: _r_getattr(op, route, 'None'),
            reverse=int(order)
        )

    def _reorder_quantity(self, quants, product_id):
        """ Reorders quants by product quantity in locations and location priority
        """
        default_route = 'name'  # i.e. location_id.name

        route = self.env.user.company_id.outgoing_routing_strategy
        order = self.env.user.company_id.outgoing_routing_order

        base, field = route.split('.', 1)
        startegy = field if base in ('location_id') else default_route

        required = self.env.context.get('reservation_quantity', 0)
        rounding = product_id.uom_id.rounding

        locations = {}
        queues = [self.env['stock.quant'], self.env['stock.quant']]  # (lprio, hprio)

        for quant in quants:
            locations.setdefault(quant.location_id, []).append(quant)

        for location in sorted(locations,
            key=lambda location: getattr(location, startegy, 'None'),
            reverse=int(order)
        ):
            location_quants = locations.get(location)
            quantity = sum([qt.quantity - qt.reserved_quantity for qt in location_quants])
            priority = float_compare(quantity, required, precision_rounding=rounding) >= 0
            for location_quant in location_quants:
                queues[priority] |= location_quant

        return queues[True] + queues[False]
