# Copyright 2026 - Today Kidekoop
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.tests import tagged
from odoo.tests.common import TransactionCase


@tagged("post_install", "-at_install")
class TestTotalMargin(TransactionCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        # Create a product with last_purchase_price
        cls.product = cls.env["product.product"].create(
            {
                "name": "Test Product Total Margin",
                "default_code": "TEST-MARGIN-001",
                "list_price": 100.0,
                "last_purchase_price_received": 4.68,
            }
        )

        # Create tax (required for some price calculations)
        cls.tax = cls.env["account.tax"].create(
            {
                "name": "Test Tax 21%",
                "amount": 21.0,
                "amount_type": "percent",
                "type_tax_use": "sale",
            }
        )
        cls.product.taxes_id = [(6, 0, [cls.tax.id])]

        # Create base pricelist with last_purchase_price
        cls.pricelist_base = cls.env["product.pricelist"].create(
            {
                "name": "Base Pricelist (Last Purchase Price)",
                "currency_id": cls.env.company.currency_id.id,
            }
        )

        # Create rule with -5% discount (simulating "cesta básica")
        cls.item_base = cls.env["product.pricelist.item"].create(
            {
                "pricelist_id": cls.pricelist_base.id,
                "applied_on": "3_global",
                "base": "last_purchase_price",
                "compute_price": "formula",
                "price_discount": 5.0,  # 5% discount
            }
        )

        # Create chained pricelist with 25% markup (simulating category margin)
        cls.pricelist_chained = cls.env["product.pricelist"].create(
            {
                "name": "Chained Pricelist (Category Margin)",
                "currency_id": cls.env.company.currency_id.id,
            }
        )

        cls.item_chained = cls.env["product.pricelist.item"].create(
            {
                "pricelist_id": cls.pricelist_chained.id,
                "applied_on": "3_global",
                "base": "pricelist",
                "base_pricelist_id": cls.pricelist_base.id,
                "compute_price": "formula",
                "price_discount": -25.0,  # 25% markup (negative discount)
                "use_total_margin": False,  # Will be toggled in tests
            }
        )

    def test_compound_margin_default_behavior(self):
        """Test that without use_total_margin, margins are compounded (standard Odoo)."""
        # Ensure use_total_margin is False
        self.item_chained.use_total_margin = False

        # Get price through chained pricelist
        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation (compound):
        # Base: 4.68
        # After -5% discount: 4.68 * 0.95 = 4.446
        # After 25% markup: 4.446 * 1.25 = 5.5575
        expected_price = 4.68 * 0.95 * 1.25
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Compound margin should be {expected_price}, got {price}",
        )

    def test_total_margin_additive_behavior(self):
        """Test that with use_total_margin=True, margins are added instead of compounded."""
        # Enable total margin
        self.item_chained.use_total_margin = True

        # Get price through chained pricelist
        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation (total/additive):
        # Base: 4.68
        # Total margin: -5% + 25% = 20%
        # Final: 4.68 * 1.20 = 5.616
        expected_price = 4.68 * 1.20
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Total margin should be {expected_price}, got {price}",
        )

    def test_total_margin_with_price_round(self):
        """Test that price_round is applied correctly with total margin."""
        # Enable total margin and set rounding
        self.item_chained.use_total_margin = True
        self.item_chained.price_round = 0.05  # Round to nearest 0.05

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Base calculation: 4.68 * 1.20 = 5.616
        # Rounded to nearest 0.05 = 5.60
        expected_price = 5.60
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Rounded total margin should be {expected_price}, got {price}",
        )

    def test_total_margin_with_surcharge(self):
        """Test that price_surcharge is applied correctly with total margin."""
        # Enable total margin and set surcharge
        self.item_chained.use_total_margin = True
        self.item_chained.price_surcharge = 1.0  # Add 1€

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Base calculation: 4.68 * 1.20 = 5.616
        # Plus surcharge: 5.616 + 1.0 = 6.616
        expected_price = 5.616 + 1.0
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Total margin with surcharge should be {expected_price}, got {price}",
        )

    def test_total_margin_three_level_chain(self):
        """Test total margin with 3 pricelists in chain."""
        # Create a third pricelist in the chain
        pricelist_3rd = self.env["product.pricelist"].create(
            {
                "name": "Third Level Pricelist",
                "currency_id": self.env.company.currency_id.id,
            }
        )

        _item_3rd = self.env["product.pricelist.item"].create(  # noqa: F841
            {
                "pricelist_id": pricelist_3rd.id,
                "applied_on": "3_global",
                "base": "pricelist",
                "base_pricelist_id": self.pricelist_chained.id,
                "compute_price": "formula",
                "price_discount": -10.0,  # 10% markup
                "use_total_margin": True,
            }
        )

        # Also enable on chained
        self.item_chained.use_total_margin = True

        price = pricelist_3rd._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation (3-level total):
        # Base: 4.68
        # Total margin: -5% + 25% + 10% = 30%
        # Final: 4.68 * 1.30 = 6.084
        expected_price = 4.68 * 1.30
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"3-level total margin should be {expected_price}, got {price}",
        )

    def test_total_margin_with_list_price_base(self):
        """Test total margin when base is list_price instead of last_purchase_price."""
        # Create new base pricelist with list_price
        pricelist_list = self.env["product.pricelist"].create(
            {
                "name": "List Price Base Pricelist",
                "currency_id": self.env.company.currency_id.id,
            }
        )

        _item_list = self.env["product.pricelist.item"].create(  # noqa: F841
            {
                "pricelist_id": pricelist_list.id,
                "applied_on": "3_global",
                "base": "list_price",
                "compute_price": "formula",
                "price_discount": 10.0,  # 10% discount
            }
        )

        # Create chained pricelist
        pricelist_chained_list = self.env["product.pricelist"].create(
            {
                "name": "Chained from List Price",
                "currency_id": self.env.company.currency_id.id,
            }
        )

        _item_chained_list = self.env["product.pricelist.item"].create(  # noqa: F841
            {
                "pricelist_id": pricelist_chained_list.id,
                "applied_on": "3_global",
                "base": "pricelist",
                "base_pricelist_id": pricelist_list.id,
                "compute_price": "formula",
                "price_discount": -20.0,  # 20% markup
                "use_total_margin": True,
            }
        )

        price = pricelist_chained_list._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation:
        # Base: 100.0 (list_price)
        # Total margin: -10% + 20% = 10%
        # Final: 100.0 * 1.10 = 110.0
        expected_price = 100.0 * 1.10
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Total margin from list_price should be {expected_price}, got {price}",
        )

    def test_total_margin_with_global_minimum(self):
        """Test that global minimum margin is enforced."""
        # Set global minimum margin to 25%
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "25.0"
        )

        # Enable total margin (calculated: -5% + 25% = 20%, below min 25%)
        self.item_chained.use_total_margin = True

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected: Base 4.68 * (1 + 25%) = 5.85 (forced to minimum)
        # Not the calculated 20%: 4.68 * 1.20 = 5.616
        expected_price = 4.68 * 1.25
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Global minimum margin should force price to {expected_price}, got {price}",
        )

        # Clean up
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "0.0"
        )

    def test_total_margin_with_global_maximum(self):
        """Test that global maximum margin is enforced."""
        # Set global maximum margin to 15%
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "15.0"
        )

        # Enable total margin (calculated: -5% + 25% = 20%, above max 15%)
        self.item_chained.use_total_margin = True

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected: Base 4.68 * (1 + 15%) = 5.382 (capped at maximum)
        # Not the calculated 20%: 4.68 * 1.20 = 5.616
        expected_price = 4.68 * 1.15
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Global maximum margin should cap price at {expected_price}, got {price}",
        )

        # Clean up
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "0.0"
        )

    def test_total_margin_with_both_limits(self):
        """Test that both min and max limits can work together."""
        # Set both limits: min 10%, max 30%
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "10.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "30.0"
        )

        # Test within range (calculated: -5% + 25% = 20%, within [10%, 30%])
        self.item_chained.use_total_margin = True
        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Should use calculated margin: 4.68 * 1.20 = 5.616
        expected_price = 4.68 * 1.20
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Margin within limits should not be adjusted: {expected_price}, got {price}",
        )

        # Clean up
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "0.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "0.0"
        )

    def test_total_margin_markup_type(self):
        """Test total margin with Markup calculation (on cost)."""
        # Enable total margin and set to markup
        self.item_base.use_total_margin = True
        self.item_chained.use_total_margin = True
        self.item_chained.margin_type = "markup"

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation (Markup):
        # Base: 4.68
        # Total margin: -5% + 25% = 20%
        # Markup formula: PVP = Cost × (1 + markup%)
        # Final: 4.68 * (1 + 0.20) = 5.616
        expected_price = 4.68 * 1.20
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Markup calculation should give {expected_price}, got {price}",
        )

    def test_total_margin_commercial_margin_type(self):
        """Test total margin with Commercial Margin calculation (on PVP)."""
        # Enable total margin and set to commercial margin
        self.item_base.use_total_margin = True
        self.item_chained.use_total_margin = True
        self.item_chained.margin_type = "margin"

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected calculation (Commercial Margin):
        # Base: 4.68
        # Total margin: -5% + 25% = 20%
        # Commercial margin formula: PVP = Cost / (1 - margin%)
        # Final: 4.68 / (1 - 0.20) = 5.85
        expected_price = 4.68 / 0.80
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Commercial margin calculation should give {expected_price}, got {price}",
        )

        # Verify the commercial margin is indeed 20%
        # Commercial margin = (PVP - Cost) / PVP
        commercial_margin = (price - 4.68) / price
        self.assertAlmostEqual(
            commercial_margin,
            0.20,
            places=4,
            msg=f"Commercial margin should be 20%, got {commercial_margin * 100}%",
        )

    def test_global_minimum_with_commercial_margin_type(self):
        """Test global minimum margin with commercial margin calculation."""
        # Set global minimum to 25% and type to commercial margin
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "25.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.global_margin_type", "margin"
        )

        # Enable total margin (calculated: -5% + 25% = 20%, below min 25%)
        self.item_chained.use_total_margin = True
        self.item_chained.margin_type = "markup"  # Item uses markup

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected: Global limit is interpreted as commercial margin
        # Min 25% commercial margin: PVP = Cost / (1 - 0.25) = 4.68 / 0.75 = 6.24
        # Item calculates 20% markup: 4.68 * 1.20 = 5.616, but global min forces 6.24
        expected_price = 4.68 / 0.75
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Global minimum (commercial margin 25%) should give {expected_price}, got {price}",
        )

        # Verify the actual commercial margin is 25%
        commercial_margin = (price - 4.68) / price
        self.assertAlmostEqual(
            commercial_margin,
            0.25,
            places=4,
            msg=f"Commercial margin should be 25%, got {commercial_margin * 100}%",
        )

        # Clean up
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.min_percent", "0.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.global_margin_type", "markup"
        )

    def test_global_maximum_with_commercial_margin_type(self):
        """Test global maximum margin with commercial margin calculation."""
        # Set global maximum to 15% and type to commercial margin
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "15.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.global_margin_type", "margin"
        )

        # Enable total margin (calculated: -5% + 25% = 20%, above max 15%)
        self.item_chained.use_total_margin = True
        self.item_chained.margin_type = "margin"  # Item uses commercial margin

        price = self.pricelist_chained._get_product_price(
            product=self.product,
            quantity=1.0,
        )

        # Expected: Global limit is interpreted as commercial margin
        # Max 15% commercial margin: PVP = Cost / (1 - 0.15) = 4.68 / 0.85 = 5.506
        # Item calculates 20% margin: 4.68 / 0.80 = 5.85, but global max limits to 5.506
        expected_price = 4.68 / 0.85
        self.assertAlmostEqual(
            price,
            expected_price,
            places=2,
            msg=f"Global maximum (commercial margin 15%) should give {expected_price}, got {price}",
        )

        # Verify the actual commercial margin is 15%
        commercial_margin = (price - 4.68) / price
        self.assertAlmostEqual(
            commercial_margin,
            0.15,
            places=4,
            msg=f"Commercial margin should be 15%, got {commercial_margin * 100}%",
        )

        # Clean up
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.max_percent", "0.0"
        )
        self.env["ir.config_parameter"].sudo().set_param(
            "product_pricelist_total_margin.global_margin_type", "markup"
        )
