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

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


class StockLocation(models.Model):
    _inherit = 'stock.location'

    is_subcontracting_location = fields.Boolean(
        "Is a Subcontracting Location?",
        help="Check this box to create a new dedicated subcontracting location for this company. Note that standard subcontracting routes will be adapted so as to take these into account automatically."
    )

    subcontractor_ids = fields.One2many('res.partner', 'property_stock_subcontractor')

    @api.constrains('is_subcontracting_location', 'usage', 'location_id')
    def _check_subcontracting_location(self):
        for location in self:
            if location == location.company_id.subcontracting_location_id:
                raise ValidationError(_("You cannot alter the company's subcontracting location"))
            if location.is_subcontracting_location and location.usage != 'internal':
                raise ValidationError(_("In order to manage stock accurately, subcontracting locations must be type Internal, linked to the appropriate company."))

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        new_subcontracting_locations = res.filtered(lambda l: l.is_subcontracting_location)
        new_subcontracting_locations._activate_subcontracting_location_rules()
        return res

    def write(self, values):
        res = super().write(values)
        if 'is_subcontracting_location' in values:
            if values['is_subcontracting_location']:
                self._activate_subcontracting_location_rules()
            else:
                self._archive_subcontracting_location_rules()
        return res

    def _check_access_putaway(self):
        """ Use sudo mode for subcontractor """
        if self.env.user.partner_id.is_subcontractor:
            return self.sudo()
        else:
            return super()._check_access_putaway()

    def _activate_subcontracting_location_rules(self):
        """ Create or unarchive rules for the 'custom' subcontracting location(s).
        The subcontracting location defined on the company is considered as the 'reference' one.
        All rules defined on this 'reference' location will be replicated on 'custom' subcontracting locations.
        """
        locations_per_company = {}
        for location in self:
            if location.is_subcontracting_location and location != location.company_id.subcontracting_location_id:
                locations_per_company.setdefault(location.company_id, []).extend(location)
        new_rules_vals = []
        rules_to_unarchive = self.env['stock.rule']
        for company, locations in locations_per_company.items():
            reference_location_id = company.subcontracting_location_id
            if reference_location_id:
                reference_rules_from = self.env['stock.rule'].search([('location_src_id', '=', reference_location_id.id)])
                reference_rules_to = self.env['stock.rule'].search([('location_dest_id', '=', reference_location_id.id)])
                for location in locations:
                    existing_rules = {
                        (rule.route_id, rule.picking_type_id, rule.action, rule.location_src_id): rule
                        for rule in self.env['stock.rule'].with_context(active_test=False).search([('location_src_id', '=', location.id)])
                    }
                    for rule in reference_rules_from:
                        if (rule.route_id, rule.picking_type_id, rule.action, location) not in existing_rules:
                            new_rules_vals.append(rule.copy_data({
                                'location_src_id': location.id,
                                'name': rule.name.replace(reference_location_id.name, location.name)
                            })[0])
                        else:
                            existing_rule = existing_rules[(rule.route_id, rule.picking_type_id, rule.action, location)]
                            if not existing_rule.active:
                                rules_to_unarchive += existing_rule
                    existing_rules = {
                        (rule.route_id, rule.picking_type_id, rule.action, rule.location_dest_id): rule
                        for rule in self.env['stock.rule'].with_context(active_test=False).search([('location_dest_id', '=', location.id)])
                    }
                    for rule in reference_rules_to:
                        if (rule.route_id, rule.picking_type_id, rule.action, location) not in existing_rules:
                            new_rules_vals.append(rule.copy_data({
                                'location_dest_id': location.id,
                                'name': rule.name.replace(reference_location_id.name, location.name)
                            })[0])
                        else:
                            existing_rule = existing_rules[(rule.route_id, rule.picking_type_id, rule.action, location)]
                            if not existing_rule.active:
                                rules_to_unarchive += existing_rule
        self.env['stock.rule'].create(new_rules_vals)
        rules_to_unarchive.action_unarchive()

    def _archive_subcontracting_location_rules(self):
        """ Archive subcontracting rules for locations that are no longer 'custom' subcontracting locations."""
        reference_location_ids = self.company_id.subcontracting_location_id
        reference_rules = self.env['stock.rule'].search(['|', ('location_src_id', 'in', reference_location_ids.ids), ('location_dest_id', 'in', reference_location_ids.ids)])
        reference_routes = reference_rules.route_id
        rules_to_archive = self.env['stock.rule'].search(['&', ('route_id', 'in', reference_routes.ids), '|', ('location_src_id', 'in', self.ids), ('location_dest_id', 'in', self.ids)])
        rules_to_archive.action_archive()
