# 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 TestStockMove(TransactionCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()

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

        # Create pricelist
        cls.pricelist = cls.env["product.pricelist"].create(
            {
                "name": "Test Pricelist",
                "currency_id": cls.env.company.currency_id.id,
            }
        )

        cls.pricelist_item = 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),
        )

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

        # Create product with UoM
        cls.uom_unit = cls.env.ref("uom.product_uom_unit")
        cls.uom_dozen = cls.env.ref("uom.product_uom_dozen")

        cls.product = cls.env["product.product"].create(
            {
                "name": "Test Product Stock",
                "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": 5.0,
                "last_purchase_price_compute_type": "without_discounts",
            }
        )

        # Create locations
        cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
        cls.stock_location = cls.env.ref("stock.stock_location_stock")

    def _create_purchase_order(
        self, product, qty, price, discount1=0, discount2=0, discount3=0
    ):
        """Helper to create a purchase order"""
        purchase_order = self.env["purchase.order"].create(
            {
                "partner_id": self.supplier.id,
            }
        )

        po_line = self.env["purchase.order.line"].create(
            {
                "order_id": purchase_order.id,
                "name": product.name or "Purchase Line",
                "product_id": product.id,
                "product_qty": qty,
                "price_unit": price,
                "product_uom": product.uom_po_id.id,
            }
        )

        # Add discounts if module supports it
        if hasattr(po_line, "discount1"):
            po_line.discount1 = discount1
        if hasattr(po_line, "discount2"):
            po_line.discount2 = discount2
        if hasattr(po_line, "discount3"):
            po_line.discount3 = discount3

        return purchase_order

    def test_update_price_without_discounts(self):
        """Test price update without discounts"""
        purchase_order = self._create_purchase_order(self.product, qty=10, price=8.0)
        purchase_order.button_confirm()

        # Get the picking and process it
        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # Verify price was updated
        self.product.invalidate_recordset()
        self.assertEqual(self.product.last_purchase_price_received, 8.0)

    def test_full_flow_updates_theoretical_price(self):
        """Test that validating a purchase receipt updates theoretical price."""
        # Initial state
        self.product.write(
            {
                "last_purchase_price_received": 0.0,
                "list_price_theoritical": 0.0,
            }
        )
        self.product.invalidate_recordset()

        # Create and confirm purchase order at 100.0
        purchase_order = self._create_purchase_order(self.product, qty=5, price=100.0)
        purchase_order.button_confirm()

        # Validate the picking
        picking = purchase_order.picking_ids[0]
        picking.action_assign()
        for move in picking.move_ids:
            move.quantity = move.product_uom_qty
        picking.button_validate()

        # Re-read from DB
        self.product.invalidate_recordset()

        # Verify last_purchase_price_received was updated
        self.assertEqual(
            self.product.last_purchase_price_received,
            100.0,
            "last_purchase_price_received should be 100.0 after receipt",
        )

        # Verify theoretical price was calculated (100.0 * 1.5 = 150.0 with 50% markup)
        self.assertAlmostEqual(
            self.product.list_price_theoritical,
            150.0,
            places=2,
            msg=f"Theoretical price should be 150.0, got {self.product.list_price_theoritical}",
        )

    def test_update_price_with_first_discount(self):
        """Test price update with first discount only"""
        if not hasattr(self.env["purchase.order.line"], "discount1"):
            self.skipTest("Purchase discount module not installed")

        self.product.last_purchase_price_compute_type = "with_discount"

        purchase_order = self._create_purchase_order(
            self.product, qty=10, price=10.0, discount1=20.0
        )
        purchase_order.button_confirm()

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # Expected: 10.0 * (1 - 0.20) = 8.0
        self.assertEqual(self.product.last_purchase_price_received, 8.0)

    def test_update_price_with_two_discounts(self):
        """Test price update with two discounts"""
        if not hasattr(self.env["purchase.order.line"], "discount2"):
            self.skipTest("Purchase double discount module not installed")

        self.product.last_purchase_price_compute_type = "with_two_discounts"

        purchase_order = self._create_purchase_order(
            self.product, qty=10, price=10.0, discount1=20.0, discount2=10.0
        )
        purchase_order.button_confirm()

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # Expected: 10.0 * (1 - 0.20) * (1 - 0.10) = 7.2
        self.assertEqual(self.product.last_purchase_price_received, 7.2)

    def test_update_price_with_three_discounts(self):
        """Test price update with three discounts"""
        if not hasattr(self.env["purchase.order.line"], "discount3"):
            self.skipTest("Purchase triple discount module not installed")

        self.product.last_purchase_price_compute_type = "with_three_discounts"

        purchase_order = self._create_purchase_order(
            self.product,
            qty=10,
            price=10.0,
            discount1=20.0,
            discount2=10.0,
            discount3=5.0,
        )
        purchase_order.button_confirm()

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # Price should be calculated from subtotal / qty
        # Subtotal with all discounts applied

    def test_update_price_with_uom_conversion(self):
        """Test price update with different purchase UoM"""
        # Create product with different purchase UoM
        product_dozen = self.product.copy(
            {
                "name": "Test Product Dozen",
                "uom_po_id": self.uom_dozen.id,
                "taxes_id": [(6, 0, [self.tax.id])],
                "last_purchase_price_compute_type": "without_discounts",
            }
        )

        purchase_order = self._create_purchase_order(
            product_dozen, qty=2, price=120.0  # 2 dozens at 120.0 per dozen
        )
        purchase_order.button_confirm()

        if not purchase_order.picking_ids:
            self.skipTest("Purchase order did not generate picking")

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # Price should be converted to base UoM (unit)
        # 120.0 per dozen = 10.0 per unit
        self.assertEqual(product_dozen.last_purchase_price_received, 10.0)

    def test_no_update_with_zero_quantity(self):
        """Zero-quantity receipt must not change last_purchase_price_received.

        Confirming the PO creates a supplierinfo line which syncs the cost to
        15.0 (no discounts).  The stock-move path must NOT overwrite that value
        when no units are actually received.
        """
        purchase_order = self._create_purchase_order(self.product, qty=10, price=15.0)
        purchase_order.button_confirm()

        # Supplierinfo sync sets the price to 15.0 after PO confirmation.
        price_after_confirm = self.product.last_purchase_price_received

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = 0

        # Stock move with qty=0 must leave the price untouched.
        self.assertEqual(self.product.last_purchase_price_received, price_after_confirm)

    def test_manual_update_type_no_automatic_update(self):
        """Test that manual update type prevents automatic price updates"""
        self.product.last_purchase_price_compute_type = "manual_update"

        purchase_order = self._create_purchase_order(self.product, qty=10, price=15.0)
        purchase_order.button_confirm()

        picking = purchase_order.picking_ids[0]
        picking.action_assign()

        for move in picking.move_ids:
            move.quantity = move.product_uom_qty

        picking.button_validate()

        # For manual update, the standard Odoo behavior applies
        # which may or may not update standard_price, but our custom
        # last_purchase_price_received logic still runs
