from odoo import api, fields, models


class AccPriorityGroup(models.Model):
    _name = "acc.priority.group"
    _description = "Groupe de clés de répartition"
    _rec_name = "display_name"
    _order = "sequence, id"

    # ------------------------------------------------------
    # Fields declaration
    # ------------------------------------------------------

    acc_operation_id = fields.Many2one("acc.operation", "Opération", required=True)
    sequence = fields.Integer(required=True, default=1)
    type_algo = fields.Selection(
        [
            ("prorata", "Répartition au prorata"),
            ("quotepart", "Répartition par quote-part"),
        ],
        string="Répartition au sein du groupe",
        default="prorata",
        required=True,
    )
    display_name = fields.Char(
        compute="_compute_display_name", store=True, readonly=True
    )
    acc_priority_group_counter_ids = fields.One2many(
        comodel_name="acc.priority.group.counter",
        inverse_name="acc_priority_group_id",
        string="Affectation de compteur par groupes de priorité",
        required=True,
    )

    counter_datas = fields.Json(compute="_compute_counter_datas")
    total_share = fields.Float(compute="_compute_total_share", store=True)
    # ------------------------------------------------------
    # SQL Constraints
    # ------------------------------------------------------

    # ------------------------------------------------------
    # Default methods
    # ------------------------------------------------------

    # ------------------------------------------------------
    # Computed fields / Search Fields
    # ------------------------------------------------------
    @api.depends("acc_priority_group_counter_ids", "acc_priority_group_counter_ids.acc_counter_share")
    def _compute_total_share(self):
        for record in self:
            record.total_share = sum(
                record.acc_priority_group_counter_ids.mapped("acc_counter_share")
            )

    @api.depends("type_algo", "sequence")
    def _compute_display_name(self):
        for prio_group in self:
            prio_group.display_name = f"Priorité {str(prio_group.sequence)}"

    def _compute_counter_datas(self):
        for priority in self:
            priority.counter_datas = (
                priority.acc_priority_group_counter_ids.acc_counter_id.mapped(
                    lambda q: {
                        "name": q.name,
                        "partner": q.partner_id.name,
                        "street": q.street,
                    }
                )
            )

    # ------------------------------------------------------
    # Onchange / Constraints
    # ------------------------------------------------------

    # ------------------------------------------------------
    # CRUD methods (ORM overrides)
    # ------------------------------------------------------
    def write(self, vals):
        seq = vals.get("sequence")
        if seq is not None:
            vals["sequence"] = seq + 1
        res = super().write(vals)

        return res

    @api.model
    def unlink(self, _id):
        self = self.env["acc.priority.group"].browse(_id)
        current_operation_id = self.env.context.get("active_id")
        existing_groups = self.env["acc.priority.group"].search(
            [("acc_operation_id", "=", current_operation_id), ("id", "!=", _id)]
        )
        seq = 0
        for group in existing_groups:
            group.sequence = seq
            seq += 1

        return super().unlink()

    @api.model_create_multi
    def create(self, vals_list):
        """
        si c est le premier groupe cree on y affecte tout les compteurs
        """
        current_operation_id = self.env.context.get("active_id")

        existing_groups = self.env["acc.priority.group"].search(
            [
                ("acc_operation_id", "=", current_operation_id),
            ]
        )

        res = super().create(vals_list)

        if existing_groups:
            res.sequence = max(existing_groups.mapped("sequence"))
            return res

        counters_to_affect = self.env["acc.counter"].search(
            [
                ("acc_operation_id", "=", res.acc_operation_id.id),
                ("type", "in", ["del", "del_inj"]),
            ]
        )
        for counter in counters_to_affect:
            res.add_counter(counter_id=counter)
        return res

    def add_counter(self, counter_id):
        self.env["acc.priority.group.counter"].create(
            {
                "acc_priority_group_id": self.id,
                "acc_operation_id": self.acc_operation_id.id,
                "acc_counter_id": counter_id.id,
                "counter_street": counter_id.street,
                "counter_owner": counter_id.partner_id.name,
            }
        )

    # ------------------------------------------------------
    # Actions
    # ------------------------------------------------------

    def compute(self, data=None):
        compute_algo = {
            "prorata": self._prorata,
            "quotepart": self._quotepart,
        }
        d = compute_algo[self.type_algo](data)
        return d

    def get_conso_sum(self, data_slot):
        conso_sum = 0.0
        for counter in self.acc_priority_group_counter_ids.acc_counter_id.mapped(
            "name"
        ):
            conso_sum += data_slot.get("conso").get(counter, 0)
        return conso_sum

    def _prorata(self, data):
        for slot in data:
            remaining_prod = data.get(slot).get("prod_totale")
            total_conso = self.get_conso_sum(data.get(slot))

            total_affecte = 0.0
            if not data.get(slot).get("affect"):
                data[slot]["affect"] = {}

            for counter in self.acc_priority_group_counter_ids.acc_counter_id:
                # un compteur peut être présent dans deux groupes de priorité
                already_affected = data.get(slot).get("affect").get(counter, 0)
                conso_k = data.get(slot).get("conso").get(counter.name)
                # si le compteur rentre en cours de periode il figure dans le
                # groupe de repartition mais pas dans les données brutes conso_k = None
                # dans ce cas on affecte 0
                if remaining_prod <= 0 or total_conso == 0 or conso_k is None:
                    newly_affected = 0
                    sum_affected = already_affected + 0.0
                else:
                    part_a_affecter = (remaining_prod * conso_k) / total_conso
                    sum_affected = min(conso_k, (already_affected + part_a_affecter))
                    newly_affected = sum_affected - already_affected
                data[slot]["affect"][counter] = sum_affected
                total_affecte += newly_affected

            # on met à jour la prod restant à affecter
            data[slot]["prod_totale"] = remaining_prod - total_affecte
            # possiblement on peut avoir affecté plus que la production total_affecte=496.00000000000006

        return data

    def _quotepart(self, data):
        total_share = self.total_share
        for slot in data:
            remaining_prod = data.get(slot).get("prod_totale")
            total_affecte = 0.0
            if not data.get(slot).get("affect"):
                data[slot]["affect"] = {}
            for priority_group_counter in self.acc_priority_group_counter_ids:
                counter = priority_group_counter.acc_counter_id
                already_affected = data.get(slot).get("affect").get(counter, 0)
                counter_share = priority_group_counter.acc_counter_share
                conso_k = data.get(slot).get("conso").get(counter.name)
                if (
                    remaining_prod <= 0
                    or total_share == 0
                    or conso_k is None
                    or counter_share == 0
                ):
                    newly_affected = 0
                    sum_affected = already_affected + 0.0
                else:
                    part_a_affecter = (remaining_prod * counter_share) / total_share
                    sum_affected = min(conso_k, (already_affected + part_a_affecter))
                    newly_affected = sum_affected - already_affected

                data[slot]["affect"][counter] = sum_affected
                total_affecte += newly_affected
            data[slot]["prod_totale"] = remaining_prod - total_affecte
        return data