# Copyright (C) 2020: Criptomart (https://criptomart.net)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

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


@tagged("post_install", "-at_install")
class TestSupplierinfoSync(TransactionCase):
    """Cover the supplierinfo -> last_purchase_price_received sync, which applies
    the vendor discounts according to last_purchase_price_compute_type."""

    @classmethod
    def setUpClass(cls):
        super().setUpClass()

        cls.tax = cls.env["account.tax"].create(
            {
                "name": "Test Tax 21%",
                "amount": 21.0,
                "amount_type": "percent",
                "type_tax_use": "sale",
            }
        )

        # Pricelist applies a 50% markup on the last purchase price, so the
        # theoretical price is always net_cost * 1.5 (same setup as stock tests).
        cls.pricelist = cls.env["product.pricelist"].create(
            {
                "name": "Test Pricelist",
                "currency_id": cls.env.company.currency_id.id,
            }
        )
        cls.env["product.pricelist.item"].create(
            {
                "pricelist_id": cls.pricelist.id,
                "compute_price": "formula",
                "base": "last_purchase_price",
                "price_markup": 50.0,
                "applied_on": "3_global",
            }
        )
        cls.env["ir.config_parameter"].sudo().set_param(
            "product_sale_price_from_pricelist.product_pricelist_automatic",
            str(cls.pricelist.id),
        )

        cls.supplier = cls.env["res.partner"].create(
            {"name": "Test Supplier", "supplier_rank": 1}
        )
        cls.supplier2 = cls.env["res.partner"].create(
            {"name": "Test Supplier 2", "supplier_rank": 1}
        )

        cls.uom_unit = cls.env.ref("uom.product_uom_unit")
        cls.product = cls.env["product.product"].create(
            {
                "name": "Test Product Supplierinfo",
                "uom_id": cls.uom_unit.id,
                "uom_po_id": cls.uom_unit.id,
                "list_price": 10.0,
                "standard_price": 5.0,
                "taxes_id": [(6, 0, [cls.tax.id])],
                "last_purchase_price_received": 0.0,
                "last_purchase_price_compute_type": "without_discounts",
            }
        )

        cls.has_discounts = hasattr(cls.env["product.supplierinfo"], "discount1")

    def setUp(self):
        super().setUp()
        if not self.has_discounts:
            self.skipTest("purchase_triple_discount module not installed")

    def _create_supplierinfo(
        self,
        product,
        price,
        discount1=0.0,
        discount2=0.0,
        discount3=0.0,
        sequence=1,
        partner=None,
    ):
        return self.env["product.supplierinfo"].create(
            {
                "partner_id": (partner or self.supplier).id,
                "product_tmpl_id": product.product_tmpl_id.id,
                "price": price,
                "discount1": discount1,
                "discount2": discount2,
                "discount3": discount3,
                "sequence": sequence,
            }
        )

    def test_sync_without_discounts(self):
        """without_discounts: vendor discounts are ignored, gross price is kept."""
        self.product.last_purchase_price_compute_type = "without_discounts"
        self._create_supplierinfo(self.product, price=1.53, discount1=15.0)
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 1.53, places=2
        )

    def test_sync_with_first_discount(self):
        """with_discount: only discount1 is applied."""
        self.product.last_purchase_price_compute_type = "with_discount"
        self._create_supplierinfo(
            self.product, price=10.0, discount1=20.0, discount2=10.0, discount3=5.0
        )
        # 10 * (1 - 0.20) = 8.0
        self.assertAlmostEqual(self.product.last_purchase_price_received, 8.0, places=2)

    def test_sync_with_two_discounts(self):
        """with_two_discounts: discount1 and discount2 are applied, discount3 ignored."""
        self.product.last_purchase_price_compute_type = "with_two_discounts"
        self._create_supplierinfo(
            self.product, price=10.0, discount1=20.0, discount2=10.0, discount3=5.0
        )
        # 10 * 0.80 * 0.90 = 7.2
        self.assertAlmostEqual(self.product.last_purchase_price_received, 7.2, places=2)

    def test_sync_with_three_discounts(self):
        """with_three_discounts: all three discounts are applied (aggregated)."""
        self.product.last_purchase_price_compute_type = "with_three_discounts"
        self._create_supplierinfo(
            self.product, price=10.0, discount1=20.0, discount2=10.0, discount3=5.0
        )
        # 10 * 0.80 * 0.90 * 0.95 = 6.84
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 6.84, places=2
        )

    def test_sync_with_three_discounts_reported_case(self):
        """Reproduces the reported DIS0002 case: 1.53 with a single 15% discount."""
        self.product.last_purchase_price_compute_type = "with_three_discounts"
        self._create_supplierinfo(self.product, price=1.53, discount1=15.0)
        # 1.53 * 0.85 = 1.3005 -> 1.30
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 1.30, places=2
        )

    def test_sync_updates_theoretical_price(self):
        """The sync also refreshes the theoretical price and the update flag."""
        self.product.last_purchase_price_compute_type = "with_three_discounts"
        self._create_supplierinfo(self.product, price=1.53, discount1=15.0)
        # net 1.30 * 1.5 markup = 1.95
        self.assertAlmostEqual(self.product.list_price_theoritical, 1.95, places=2)
        self.assertTrue(
            self.product.last_purchase_price_updated,
            "last_purchase_price_updated should flag a pending sale price change",
        )

    def test_manual_update_is_not_synced(self):
        """manual_update: the cost is never touched by the supplierinfo sync."""
        self.product.write(
            {
                "last_purchase_price_compute_type": "manual_update",
                "last_purchase_price_received": 99.0,
            }
        )
        self._create_supplierinfo(self.product, price=10.0, discount1=20.0)
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 99.0, places=2
        )

    def test_write_resyncs(self):
        """Editing the discount on an existing supplierinfo re-syncs the cost."""
        self.product.last_purchase_price_compute_type = "with_three_discounts"
        seller = self._create_supplierinfo(self.product, price=1.53, discount1=15.0)
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 1.30, places=2
        )
        seller.discount1 = 0.0
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 1.53, places=2
        )

    def test_secondary_seller_does_not_override(self):
        """Only the main vendor (lowest sequence) drives the cost basis."""
        self.product.last_purchase_price_compute_type = "with_three_discounts"
        self._create_supplierinfo(
            self.product, price=10.0, sequence=1, partner=self.supplier
        )
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 10.0, places=2
        )
        # A cheaper, heavily discounted secondary vendor must not change the cost.
        self._create_supplierinfo(
            self.product,
            price=5.0,
            discount1=50.0,
            sequence=5,
            partner=self.supplier2,
        )
        self.assertAlmostEqual(
            self.product.last_purchase_price_received, 10.0, places=2
        )
