# Copyright (C) 2020: Criptomart (https://criptomart.net)
# @author Santi Noreña (<santi@criptomart.net>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).


from odoo import exceptions, models, fields, api, _
from odoo.exceptions import UserError
import math


class ProductTemplate(models.Model):
    _inherit = "product.template"

    last_purchase_price_updated = fields.Boolean(
        string="Last purchase price updated",
        help="The last purchase price has been updated and you need to update the selling price in the database, shelves and scales.",
        default=False,
        company_dependent=True,
    )
    list_price_theoritical = fields.Float(
        "Theoritical price",
        company_dependent=True,
    )
    last_purchase_price_received = fields.Float(
        string="Last purchase price",
        help="The last price at which the product was purchased. It is used as the base price field for calculating the product sale price.",
        digits="Product Price",
        company_dependent=True,
    )
    last_purchase_price_compute_type = fields.Selection(
        [
            ("without_discounts", "Without discounts"),
            ("with_discount", "First discount"),
            ("with_two_discounts", "Double discount"),
            ("with_three_discounts", "Triple discount"),
            ("manual_update", "Manual update"),
        ],
        string="Last purchase price calculation type",
        help="Choose whether discounts should influence the calculation of the last purchase price. Select Never update for manual configuration of cost and sale prices.\n"
        "\n* Without discounts: does not take into account discounts when updating the last purchase price.\n"
        "* First discount: take into account only first discount when updating the last purchase price.\n"
        '* Triple discount: take into account all discounts when updating the last purchase price. Needs "Purchase Triple Discount" OCA module.\n'
        "* Manual update: Select this for manual configuration of cost and sale price. The sales price will not be calculated automatically.",
        default="without_discounts",
        required=True,
        company_dependent=True,
    )
    configured_sale_margin = fields.Float(
        string="Configured sale margin (%)",
        digits="Discount",
        compute="_compute_configured_sale_margin",
        help="Effective margin configured by the automatic pricelist chain for "
        "this product. It reflects tariff configuration, not the current "
        "manual sale price.",
    )

    def _compute_configured_sale_margin(self):
        pricelist_id = (
            self.env["ir.config_parameter"]
            .sudo()
            .get_param("product_sale_price_from_pricelist.product_pricelist_automatic")
            or False
        )
        if not pricelist_id:
            for template in self:
                template.configured_sale_margin = 0.0
            return

        pricelist = self.env["product.pricelist"].browse(int(pricelist_id))
        for template in self:
            template.configured_sale_margin = 0.0
            if (
                not template.product_variant_id
                or template.last_purchase_price_compute_type == "manual_update"
                or not template.last_purchase_price_received
            ):
                continue

            # Get the final untaxed price after all chained pricelists are applied.
            final_price = template.product_variant_id._get_price(
                qty=1,
                pricelist=pricelist,
            )
            untaxed_price = final_price.get(template.product_variant_id.id, {}).get(
                "value"
            )
            if untaxed_price is None:
                continue

            template.configured_sale_margin = (
                (untaxed_price / template.last_purchase_price_received) - 1.0
            ) * 100.0

    def _compute_theoritical_price(self):
        pricelist_obj = self.env["product.pricelist"]
        pricelist_id = (
            self.env["ir.config_parameter"]
            .sudo()
            .get_param("product_sale_price_from_pricelist.product_pricelist_automatic")
            or False
        )
        pricelist = pricelist_obj.browse(int(pricelist_id))
        if pricelist:
            for template in self:
                if (
                    template.name
                    and template.id
                    and template.product_variant_id
                    and template.last_purchase_price_compute_type != "manual_update"
                ):
                    partial_price = template.product_variant_id._get_price(
                        qty=1, pricelist=pricelist
                    )
                    # Compute taxes to add
                    if not template.taxes_id:
                        raise UserError(
                            _(
                                "No taxes defined for product %s. Please define taxes in the product form."
                            )
                            % template.name
                        )
                    tax_price = template.taxes_id.compute_all(
                        partial_price[template.product_variant_id.id]["value"] or 0.0,
                        handle_price_include=False,
                    )
                    price_with_taxes = (
                        tax_price["taxes"][0]["amount"]
                        + partial_price[template.product_variant_id.id]["value"]
                    )
                    # Round to configurable step (defined on the pricelist)
                    step = pricelist.price_rounding_step
                    if step:
                        method = pricelist.price_rounding_method
                        if method == "down":
                            price_with_taxes = (
                                math.floor(price_with_taxes / step) * step
                            )
                        elif method == "round":
                            price_with_taxes = round(price_with_taxes / step) * step
                        else:  # "up" (default)
                            price_with_taxes = math.ceil(price_with_taxes / step) * step

                    template.write(
                        {
                            "list_price_theoritical": price_with_taxes,
                            "last_purchase_price_updated": (
                                price_with_taxes != template.list_price
                            ),
                        }
                    )
        else:
            raise UserError(
                _(
                    "Not found a valid pricelist to compute sale price. Check configuration in General Settings."
                )
            )

    def action_update_list_price(self):
        for template in self:
            if template.last_purchase_price_compute_type != "manual_update":
                template.list_price = template.list_price_theoritical
                template.last_purchase_price_updated = False

    def price_compute(
        self, price_type, uom=None, currency=None, company=False, date=False
    ):
        """Return dummy not falsy prices when computation is done from supplier
        info for avoiding error on super method. We will later fill these with
        correct values.
        """
        if price_type == "last_purchase_price":
            return dict.fromkeys(self.ids, 1.0)
        return super().price_compute(
            price_type, uom=uom, currency=currency, company=company, date=date
        )
