# Copyright 2025 Criptomart
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

"""
Test suite for pricing calculations using OCA product_get_price_helper addon.

Coverage:
- add_to_eskaera_cart with pricelist
- add_to_eskaera_cart with fiscal position
- add_to_eskaera_cart with taxes
- add_to_eskaera_cart with discounts
- Fallback to list_price when pricelist fails
- Product price info structure in eskaera_shop
"""

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

from ..controllers.website_sale import AplicoopWebsiteSale


@tagged("post_install", "-at_install")
class TestPricingWithPricelist(TransactionCase):
    """Test pricing calculations using OCA product_get_price_helper addon."""

    def setUp(self):
        super().setUp()

        # Create test company
        self.company = self.env["res.company"].create(
            {
                "name": "Test Company Pricing",
            }
        )

        # Create test group
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group Pricing",
                "is_company": True,
                "company_id": self.company.id,
            }
        )

        # Create test user
        self.user = self.env["res.users"].create(
            {
                "name": "Test User Pricing",
                "login": "testpricing@example.com",
                "company_id": self.company.id,
                "company_ids": [(6, 0, [self.company.id])],
            }
        )
        self.partner = self.user.partner_id

        # Get or create default tax group
        tax_group = self.env["account.tax.group"].search(
            [("company_id", "=", self.company.id)], limit=1
        )
        if not tax_group:
            tax_group = self.env["account.tax.group"].create(
                {
                    "name": "IVA",
                    "company_id": self.company.id,
                }
            )

        # Get default country (Spain)
        country_es = self.env.ref("base.es")

        # Create tax (21% IVA)
        self.tax_21 = self.env["account.tax"].create(
            {
                "name": "IVA 21%",
                "amount": 21.0,
                "amount_type": "percent",
                "type_tax_use": "sale",
                "company_id": self.company.id,
                "country_id": country_es.id,
                "tax_group_id": tax_group.id,
            }
        )

        # Create tax (10% IVA reducido)
        self.tax_10 = self.env["account.tax"].create(
            {
                "name": "IVA 10%",
                "amount": 10.0,
                "amount_type": "percent",
                "type_tax_use": "sale",
                "company_id": self.company.id,
                "country_id": country_es.id,
                "tax_group_id": tax_group.id,
            }
        )

        # Create fiscal position (maps 21% to 10%)
        self.fiscal_position = self.env["account.fiscal.position"].create(
            {
                "name": "Test Fiscal Position",
                "company_id": self.company.id,
            }
        )
        self.env["account.fiscal.position.tax"].create(
            {
                "position_id": self.fiscal_position.id,
                "tax_src_id": self.tax_21.id,
                "tax_dest_id": self.tax_10.id,
            }
        )

        # Create product category
        self.category = self.env["product.category"].create(
            {
                "name": "Test Category Pricing",
            }
        )

        # Create test products with different tax configurations
        self.product_with_tax = self.env["product.product"].create(
            {
                "name": "Product With 21% Tax",
                "list_price": 100.0,
                "categ_id": self.category.id,
                "taxes_id": [(6, 0, [self.tax_21.id])],
                "company_id": self.company.id,
            }
        )

        self.product_without_tax = self.env["product.product"].create(
            {
                "name": "Product Without Tax",
                "list_price": 50.0,
                "categ_id": self.category.id,
                "taxes_id": False,
                "company_id": self.company.id,
            }
        )

        # Create pricelist with discount
        self.pricelist_with_discount = self.env["product.pricelist"].create(
            {
                "name": "Test Pricelist 10% Discount",
                "company_id": self.company.id,
                "item_ids": [
                    (
                        0,
                        0,
                        {
                            "compute_price": "percentage",
                            "percent_price": 10.0,  # 10% discount
                            "applied_on": "3_global",
                        },
                    )
                ],
            }
        )

        # Create pricelist without discount
        self.pricelist_no_discount = self.env["product.pricelist"].create(
            {
                "name": "Test Pricelist No Discount",
                "company_id": self.company.id,
            }
        )

        # Controller helper
        self.controller = AplicoopWebsiteSale()

        # Create group order
        self.group_order = self.env["group.order"].create(
            {
                "name": "Test Order Pricing",
                "state": "open",
                "group_ids": [(6, 0, [self.group.id])],
                "product_ids": [
                    (6, 0, [self.product_with_tax.id, self.product_without_tax.id])
                ],
                "company_id": self.company.id,
            }
        )

    def test_add_to_cart_basic_price_without_tax(self):
        """Test basic price calculation for product without taxes."""
        # Product without taxes should return list_price
        result = self.product_without_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        self.assertEqual(
            result["value"], 50.0, "Product without tax should have price = list_price"
        )
        self.assertEqual(
            result.get("discount", 0),
            0,
            "No discount pricelist should have 0% discount",
        )

    def test_add_to_cart_with_pricelist_discount(self):
        """Test that discounted prices are calculated correctly."""
        # 10% discount on 100.0 = 90.0
        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_with_discount,
            fposition=False,
        )

        # OCA addon returns price without taxes by default
        expected_price = 100.0 * 0.9  # 90.0

        self.assertIn("value", result, "Result must contain 'value' key")
        self.assertIn("tax_included", result, "Result must contain 'tax_included' key")
        self.assertAlmostEqual(
            result["value"],
            expected_price,
            places=2,
            msg=f"Expected {expected_price}, got {result['value']}",
        )

    def test_add_to_cart_with_fiscal_position(self):
        """Test fiscal position maps taxes correctly (21% -> 10%)."""
        # With fiscal position: 21% tax becomes 10% tax
        result_without_fp = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        result_with_fp = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=self.fiscal_position,
        )

        # Both should return base price (100.0) without tax by default
        # Tax mapping only affects tax calculation, not the base price returned
        self.assertIn("value", result_without_fp, "Result must contain 'value'")
        self.assertIn("value", result_with_fp, "Result must contain 'value'")
        self.assertEqual(result_without_fp["value"], 100.0)
        self.assertEqual(result_with_fp["value"], 100.0)

    def test_add_to_cart_with_tax_included(self):
        """Test price calculation returns tax_included flag correctly."""
        # Product with 21% tax
        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        # By default, tax is not included in price
        self.assertIn("tax_included", result)
        self.assertEqual(
            result["value"], 100.0, "Price should be base price without tax"
        )

    def test_add_to_cart_with_quantity_discount(self):
        """Test quantity-based discounts if applicable."""
        # Create pricelist with quantity-based rule
        pricelist_qty = self.env["product.pricelist"].create(
            {
                "name": "Quantity Discount Pricelist",
                "company_id": self.company.id,
                "item_ids": [
                    (
                        0,
                        0,
                        {
                            "compute_price": "percentage",
                            "percent_price": 20.0,  # 20% discount
                            "min_quantity": 5.0,
                            "applied_on": "3_global",
                        },
                    )
                ],
            }
        )

        # Quantity 1: No discount
        result_qty_1 = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=pricelist_qty,
            fposition=False,
        )

        # Quantity 5: 20% discount
        result_qty_5 = self.product_with_tax._get_price(
            qty=5.0,
            pricelist=pricelist_qty,
            fposition=False,
        )

        # Qty 1: 100.0 (no discount, no tax in value)
        # Qty 5: 100 * 0.8 = 80.0 (with 20% discount, no tax in value)
        self.assertAlmostEqual(result_qty_1["value"], 100.0, places=2)
        self.assertAlmostEqual(result_qty_5["value"], 80.0, places=2)

    def test_add_to_cart_price_fallback_no_pricelist(self):
        """Test fallback to list_price when pricelist is not available."""
        # Simulate no pricelist scenario
        # OCA addon should handle this gracefully
        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=False,
            fposition=False,
        )

        # Should return list_price with taxes (fallback behavior)
        # This depends on OCA addon implementation
        self.assertIsNotNone(result, "Should not fail when pricelist is False")
        self.assertIn("value", result, "Result should contain 'value' key")

    def test_add_to_cart_price_fallback_no_variant(self):
        """Test handling when product has no variants."""
        # Create product template without variants
        product_template = self.env["product.template"].create(
            {
                "name": "Product Without Variant",
                "list_price": 75.0,
                "categ_id": self.category.id,
                "company_id": self.company.id,
            }
        )

        # Should have auto-created variant
        self.assertTrue(
            product_template.product_variant_ids,
            "Product template should have at least one variant",
        )

        variant = product_template.product_variant_ids[0]
        result = variant._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        self.assertIsNotNone(result, "Should handle product with auto-created variant")
        self.assertAlmostEqual(result["value"], 75.0, places=2)

    def test_product_price_info_structure(self):
        """Test product_price_info dict structure returned by _get_price."""
        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_with_discount,
            fposition=False,
        )

        # Verify structure
        self.assertIn("value", result, "Result must contain 'value' key")
        self.assertIsInstance(
            result["value"], (int, float), "Price value must be numeric"
        )

        # Optional keys (depends on OCA addon version)
        if "discount" in result:
            self.assertIsInstance(result["discount"], (int, float))

        if "original_value" in result:
            self.assertIsInstance(result["original_value"], (int, float))

    def test_discounted_price_visual_comparison(self):
        """Test comparison of original vs discounted price for UI display."""
        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_with_discount,
            fposition=False,
        )

        # When there's a discount, original_value should be higher than value
        if result.get("discount", 0) > 0:
            original = result.get("original_value", result["value"])
            discounted = result["value"]
            self.assertGreater(
                original,
                discounted,
                "Original price should be higher than discounted price",
            )

    def test_price_calculation_with_multiple_taxes(self):
        """Test product with multiple taxes applied."""
        # Get tax group and country from existing tax
        tax_group = self.tax_21.tax_group_id
        country = self.tax_21.country_id

        # Create additional tax
        tax_extra = self.env["account.tax"].create(
            {
                "name": "Extra Tax 5%",
                "amount": 5.0,
                "amount_type": "percent",
                "type_tax_use": "sale",
                "company_id": self.company.id,
                "country_id": country.id,
                "tax_group_id": tax_group.id,
            }
        )

        product_multi_tax = self.env["product.product"].create(
            {
                "name": "Product With Multiple Taxes",
                "list_price": 100.0,
                "categ_id": self.category.id,
                "taxes_id": [(6, 0, [self.tax_21.id, tax_extra.id])],
                "company_id": self.company.id,
            }
        )

        result = product_multi_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        # Base price 100.0 (taxes not included in value by default)
        self.assertEqual(
            result["value"],
            100.0,
            msg="Should return base price (taxes applied separately)",
        )

    def test_price_currency_handling(self):
        """Test price calculation with different currencies."""
        # Get or use existing EUR currency
        eur = self.env["res.currency"].search([("name", "=", "EUR")], limit=1)
        if not eur:
            self.skipTest("EUR currency not available in test database")

        # Create pricelist with EUR
        pricelist_eur = self.env["product.pricelist"].create(
            {
                "name": "EUR Pricelist",
                "currency_id": eur.id,
                "company_id": self.company.id,
            }
        )

        result = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=pricelist_eur,
            fposition=False,
        )

        self.assertIsNotNone(result, "Should handle different currency pricelist")
        self.assertIn("value", result)

    def test_price_consistency_across_calls(self):
        """Test that multiple calls with same params return same price."""
        result1 = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_with_discount,
            fposition=False,
        )

        result2 = self.product_with_tax._get_price(
            qty=1.0,
            pricelist=self.pricelist_with_discount,
            fposition=False,
        )

        self.assertEqual(
            result1["value"],
            result2["value"],
            "Price calculation should be deterministic",
        )

    def test_zero_price_product(self):
        """Test handling of free products (price = 0)."""
        free_product = self.env["product.product"].create(
            {
                "name": "Free Product",
                "list_price": 0.0,
                "categ_id": self.category.id,
                "company_id": self.company.id,
            }
        )

        result = free_product._get_price(
            qty=1.0,
            pricelist=self.pricelist_no_discount,
            fposition=False,
        )

        self.assertEqual(result["value"], 0.0, "Free product should have price = 0")

    def test_negative_quantity_handling(self):
        """Test that negative quantities are handled properly."""
        # Negative qty should either be rejected or handled as positive
        try:
            result = self.product_with_tax._get_price(
                qty=-1.0,
                pricelist=self.pricelist_no_discount,
                fposition=False,
            )
            # If it doesn't raise, check the result is valid
            self.assertIsNotNone(result)
        except Exception:
            # If it raises, that's also acceptable behavior
            self.assertTrue(True, "Negative quantity properly rejected")

    def test_pricing_helper_uses_config_pricelist_and_taxes(self):
        """Pricing helper must apply configured pricelist and include taxes for display."""

        website = self.env.ref("website.default_website").sudo()
        website.write(
            {
                "show_line_subtotals_tax_selection": "tax_included",
                "company_id": self.company.id,
            }
        )

        pricelist_discount = self.env["product.pricelist"].create(
            {
                "name": "Config Pricelist 10%",
                "company_id": self.company.id,
                "item_ids": [
                    (
                        0,
                        0,
                        {
                            "compute_price": "percentage",
                            "percent_price": 10.0,
                            "applied_on": "3_global",
                        },
                    )
                ],
            }
        )

        self.env["ir.config_parameter"].sudo().set_param(
            "website_sale_aplicoop.pricelist_id", pricelist_discount.id
        )

        product = self.product_with_tax  # 100€ + 21%

        pricing = self.controller._get_pricing_info(
            product,
            pricelist_discount,
            quantity=1.0,
            partner=self.partner,
        )

        # price_unit uses pricelist (10% discount)
        self.assertAlmostEqual(pricing["price_unit"], 90.0, places=2)
        # display price must include taxes (21% on discounted price)
        self.assertAlmostEqual(pricing["price"], 108.9, places=2)
        self.assertTrue(pricing.get("tax_included"))
        self.assertTrue(pricing.get("has_discounted_price"))
