# Copyright (C) 2014-Today GRAP (http://www.grap.coop)
# Copyright (C) 2016-Today La Louve (http://www.lalouve.net)
# Copyright 2017 LasLabs Inc.
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import _, api, exceptions, fields, models, tools

_GENERATE_TYPE = [
    ("no", "No generation"),
    ("manual", "Base set Manually"),
    ("sequence", "Base managed by Sequence"),
]


class BarcodeRule(models.Model):

    _inherit = "barcode.rule"

    # Column Section
    generate_type = fields.Selection(
        selection=_GENERATE_TYPE,
        required=True,
        default="no",
        help="Allow to generate barcode, including a number"
        "  (a base) in the final barcode.\n"
        " 'Base Set Manually' : User should set manually the value of the"
        " barcode base\n"
        " 'Base managed by Sequence': User will use a button to generate a"
        " new base. This base will be generated by a sequence",
    )

    generate_model = fields.Selection(
        selection=[],
        help="If 'Generate Type' is set, mention the model related to this" " rule.",
    )

    padding = fields.Integer(compute="_compute_padding", readonly=True, store=True)

    sequence_id = fields.Many2one(
        string="Generation Sequence", comodel_name="ir.sequence"
    )

    generate_automate = fields.Boolean(
        string="Automatic Generation",
        help="Check this to automatically generate a barcode upon creation of "
        "a new record in the mixed model.",
    )

    # Compute Section
    @api.depends("pattern")
    def _compute_padding(self):
        for rule in self:
            rule.padding = rule.pattern.count(".")

    # On Change Section
    @api.onchange("generate_type")
    def onchange_generate_type(self):
        for rule in self:
            if rule.generate_type == "no":
                rule.generate_model = False

    # Constrains Section
    @api.constrains("generate_model", "generate_automate")
    def _check_generate_model_automate(self):
        """It should not allow two automated barcode generators per model.
        It also clears the cache of automated rules if necessary.
        """
        for record in self:
            if not record.generate_automate:
                continue
            # This query is duplicated, but necessary because the other
            # method is cached & we need a completely current result.
            domain = [
                ("generate_model", "=", record.generate_model),
                ("generate_automate", "=", True),
            ]
            if len(self.search(domain)) > 1:
                raise exceptions.ValidationError(
                    _(
                        "Only one rule per model can be used for automatic "
                        "barcode generation."
                    )
                )

    # CRUD
    @api.model_create_multi
    def create(self, vals_list):
        self._clear_cache(vals_list)
        res = super().create(vals_list)
        res.generate_sequence_if_required()
        return res

    def write(self, vals):
        self._clear_cache(vals)
        res = super().write(vals)
        self.generate_sequence_if_required()
        return res

    def generate_sequence_if_required(self):
        IrSequence = self.env["ir.sequence"]
        rules = self.filtered(
            lambda x: x.generate_type == "sequence" and not x.sequence_id
        )
        for rule in rules:
            sequence = IrSequence.create(self._prepare_sequence(rule))
            rule.sequence_id = sequence.id

    # Custom Section
    @api.model
    def _prepare_sequence(self, rule):
        return {
            "name": _("Sequence - %s") % rule.name,
            "padding": rule.padding,
        }

    @api.model
    def get_automatic_rule(self, model):
        """It provides a cached indicator for barcode automation.

        Args:
            model (str): Name of model to search for.
        Returns:
            BarcodeRule: Recordset of automated barcode rules for model.

        """
        return self.browse(self.get_automatic_rule_ids(model))

    @api.model
    @tools.ormcache("model")
    def get_automatic_rule_ids(self, model):
        """It provides a cached indicator for barcode automation.

        Note that this cache needs to be explicitly cleared when
        `generate_automate` is changed on an associated `barcode.rule`.

        Args:
            model (str): Name of model to search for.
        Returns:
            list of int: IDs of the automated barcode rules for model.

        """
        record = self.search(
            [("generate_model", "=", model), ("generate_automate", "=", True)]
        )
        return record.ids

    @api.model
    def _clear_cache(self, vals):
        """It clears the caches if certain vals are updated."""
        fields = ("generate_model", "generate_automate")
        if any(k in vals for k in fields):
            self.invalidate_model(fields)
