import logging

from odoo import fields
from odoo import models
from odoo.exceptions import UserError
from odoo.tools import float_round

_logger = logging.getLogger(__name__)


class ProductProduct(models.Model):
    _inherit = "product.product"

    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,
    )
    list_price_theoritical = fields.Float(
        "Theoritical price",
        default=0.0,
    )
    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",
        default=0.0,
    )
    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",
    )

    # Keep this for backward compatibility with pricelist computations
    last_purchase_price = fields.Float(
        related="last_purchase_price_received",
        string="Last Purchase Price",
        readonly=True,
    )

    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 product in self:
                _logger.info(
                    "[PRICE DEBUG] Product %s [%s]: Checking conditions - name=%s, id=%s, compute_type=%s",
                    product.default_code or product.name,
                    product.id,
                    bool(product.name),
                    bool(product.id),
                    product.last_purchase_price_compute_type,
                )
                if (
                    product.name
                    and product.id
                    and product.last_purchase_price_compute_type != "manual_update"
                ):
                    # Compute taxes to add
                    if not product.taxes_id:
                        raise UserError(
                            self.env._(
                                "No taxes defined for product %(product)s. Please define taxes in the product form.",
                                product=product.name,
                            )
                        )

                    # Ask the pricelist for the price directly. Going through
                    # _get_price would compute the price twice (once for the
                    # value and once more to resolve the discount rule, which we
                    # don't use here), triggering a redundant _compute_price_rule.
                    base_price = pricelist._get_product_price(product, 1.0) or 0.0
                    _logger.info(
                        "[PRICE DEBUG] Product %s [%s]: base_price from pricelist = %.2f, last_purchase_price = %.2f",
                        product.default_code or product.name,
                        product.id,
                        base_price,
                        product.last_purchase_price_received,
                    )

                    # Use base price without taxes (taxes will be calculated automatically on sales)
                    theoretical_price = base_price

                    _logger.info(
                        "[PRICE] Product %s [%s]: Computed theoretical price %.2f (previous: %.2f, current list_price: %.2f)",
                        product.default_code or product.name,
                        product.id,
                        theoretical_price,
                        product.list_price_theoritical,
                        product.lst_price,
                    )

                    product.write(
                        {
                            "list_price_theoritical": theoretical_price,
                            "last_purchase_price_updated": (
                                theoretical_price != product.lst_price
                            ),
                        }
                    )
        else:
            raise UserError(
                self.env._(
                    "Not found a valid pricelist to compute sale price. Check configuration in General Settings."
                )
            )

    def _recompute_theoritical_price_safe(self):
        """Recompute the theoretical price without raising when the automatic
        pricelist is not configured yet (e.g. while editing supplierinfo lines
        during data imports)."""
        pricelist_id = (
            self.env["ir.config_parameter"]
            .sudo()
            .get_param("product_sale_price_from_pricelist.product_pricelist_automatic")
        )
        if not pricelist_id:
            return
        self._compute_theoritical_price()

    def _get_main_supplierinfo(self):
        """Return the main vendor supplierinfo line for this variant (first
        active vendor by sequence, same selection Odoo uses for purchasing)."""
        self.ensure_one()
        return fields.first(self._prepare_sellers())

    def _compute_supplierinfo_net_price(self, seller):
        """Net purchase price from a supplierinfo line, applying its discounts
        according to last_purchase_price_compute_type. Mirrors the receipt
        logic in stock.move so both paths produce the same cost basis."""
        self.ensure_one()
        price = seller.price or 0.0
        compute_type = self.last_purchase_price_compute_type
        if compute_type == "with_discount":
            price *= 1 - (seller.discount1 or 0.0) / 100
        elif compute_type == "with_two_discounts":
            price *= (1 - (seller.discount1 or 0.0) / 100) * (
                1 - (seller.discount2 or 0.0) / 100
            )
        elif compute_type == "with_three_discounts":
            # seller.discount aggregates the three discounts (triple discount).
            price *= 1 - (seller.discount or 0.0) / 100
        return float_round(price, precision_digits=2)

    def action_update_list_price(self):
        updated_products = []
        skipped_products = []

        for product in self:
            if product.last_purchase_price_compute_type != "manual_update":
                # First compute the theoretical price
                product._compute_theoritical_price()

                old_price = product.lst_price
                product.lst_price = product.list_price_theoritical
                product.standard_price = product.last_purchase_price_received
                product.last_purchase_price_updated = False
                _logger.info(
                    "[PRICE] Product %s [%s]: List price updated from %.2f to %.2f",
                    product.default_code or product.name,
                    product.id,
                    old_price,
                    product.lst_price,
                )
                updated_products.append(
                    (
                        product.name or product.default_code,
                        old_price,
                        product.lst_price,
                    )
                )
            else:
                skipped_products.append(product.name or product.default_code)

        # Invalidate cache to refresh UI
        self.invalidate_recordset(
            ["lst_price", "list_price_theoritical", "last_purchase_price_updated"]
        )

        # Build notification message
        message = ""
        if updated_products:
            message += self.env._(
                "✓ Products updated: %(count)s", count=len(updated_products)
            )
            if len(updated_products) <= 5:
                message += "\n\n"
                for name, old, new in updated_products:
                    message += self.env._(
                        "• %(name)s: %(old).2f → %(new).2f\n",
                        name=name,
                        old=old,
                        new=new,
                    )

        if skipped_products:
            if message:
                message += "\n\n"
            message += self.env._(
                "⚠ Skipped (manual update): %(count)s", count=len(skipped_products)
            )
            if len(skipped_products) <= 5:
                message += "\n"
                for name in skipped_products:
                    message += self.env._("• %(name)s\n", name=name)

        if not updated_products and not skipped_products:
            message = self.env._("No products to update.")

        return {
            "type": "ir.actions.client",
            "tag": "display_notification",
            "params": {
                "title": self.env._("Price Update"),
                "message": message,
                "type": "success" if updated_products else "warning",
                "sticky": False,
                "next": {"type": "ir.actions.act_window_close"},
            },
        }

    def price_compute(
        self, price_type, uom=None, currency=None, company=None, 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,
        )
