from decimal import Decimal
from django.test import TestCase
from django.core.exceptions import ValidationError

from ..models.base import Currency, Term, Unit
from ..models.providers import CloudProvider
from ..models.services import Service
from ..models.pricing import (
    ComputePlan,
    ComputePlanPrice,
    StoragePlan,
    StoragePlanPrice,
    ProgressiveDiscountModel,
    DiscountTier,
    VSHNAppCatPrice,
    VSHNAppCatBaseFee,
    VSHNAppCatUnitRate,
    VSHNAppCatAddon,
    VSHNAppCatAddonBaseFee,
    VSHNAppCatAddonUnitRate,
)


class ComputePlanTestCase(TestCase):
    """Test cases for ComputePlan model and pricing"""

    def setUp(self):
        # Create test cloud provider
        self.cloud_provider = CloudProvider.objects.create(
            name="Test Provider",
            slug="test-provider",
            description="Test cloud provider",
            website="https://test.com",
        )

        # Create test compute plan
        self.compute_plan = ComputePlan.objects.create(
            name="Small Plan",
            vcpus=2.0,
            ram=4.0,
            cpu_mem_ratio=0.5,
            cloud_provider=self.cloud_provider,
            term=Term.MTH,
        )

    def test_compute_plan_str_representation(self):
        """Test string representation of ComputePlan"""
        self.assertEqual(str(self.compute_plan), "Small Plan")

    def test_compute_plan_price_creation(self):
        """Test creation of ComputePlanPrice"""
        price = ComputePlanPrice.objects.create(
            compute_plan=self.compute_plan,
            currency=Currency.CHF,
            amount=Decimal("100.00"),
        )
        self.assertEqual(price.amount, Decimal("100.00"))
        self.assertEqual(price.currency, Currency.CHF)

    def test_compute_plan_get_price_exists(self):
        """Test get_price method when price exists"""
        ComputePlanPrice.objects.create(
            compute_plan=self.compute_plan,
            currency=Currency.CHF,
            amount=Decimal("100.00"),
        )
        price = self.compute_plan.get_price(Currency.CHF)
        self.assertEqual(price, Decimal("100.00"))

    def test_compute_plan_get_price_not_exists(self):
        """Test get_price method when price doesn't exist"""
        price = self.compute_plan.get_price(Currency.EUR)
        self.assertIsNone(price)

    def test_compute_plan_price_unique_constraint(self):
        """Test unique constraint on compute_plan and currency"""
        ComputePlanPrice.objects.create(
            compute_plan=self.compute_plan,
            currency=Currency.CHF,
            amount=Decimal("100.00"),
        )

        # Creating another price with same plan and currency should be allowed
        # (this will update the existing one)
        with self.assertRaises(Exception):
            ComputePlanPrice.objects.create(
                compute_plan=self.compute_plan,
                currency=Currency.CHF,
                amount=Decimal("200.00"),
            )


class StoragePlanTestCase(TestCase):
    """Test cases for StoragePlan model and pricing"""

    def setUp(self):
        # Create test cloud provider
        self.cloud_provider = CloudProvider.objects.create(
            name="Test Provider",
            slug="test-provider",
            description="Test cloud provider",
            website="https://test.com",
        )

        # Create test storage plan
        self.storage_plan = StoragePlan.objects.create(
            name="SSD Storage",
            cloud_provider=self.cloud_provider,
            term=Term.MTH,
            unit=Unit.GIB,
        )

    def test_storage_plan_str_representation(self):
        """Test string representation of StoragePlan"""
        self.assertEqual(str(self.storage_plan), "SSD Storage")

    def test_storage_plan_price_creation(self):
        """Test creation of StoragePlanPrice"""
        price = StoragePlanPrice.objects.create(
            storage_plan=self.storage_plan,
            currency=Currency.CHF,
            amount=Decimal("0.15"),
        )
        self.assertEqual(price.amount, Decimal("0.15"))
        self.assertEqual(price.currency, Currency.CHF)

    def test_storage_plan_get_price_exists(self):
        """Test get_price method when price exists"""
        StoragePlanPrice.objects.create(
            storage_plan=self.storage_plan,
            currency=Currency.USD,
            amount=Decimal("0.12"),
        )
        price = self.storage_plan.get_price(Currency.USD)
        self.assertEqual(price, Decimal("0.12"))

    def test_storage_plan_get_price_not_exists(self):
        """Test get_price method when price doesn't exist"""
        price = self.storage_plan.get_price(Currency.EUR)
        self.assertIsNone(price)


class ProgressiveDiscountModelTestCase(TestCase):
    """Test cases for ProgressiveDiscountModel and discount calculations"""

    def setUp(self):
        # Create discount model
        self.discount_model = ProgressiveDiscountModel.objects.create(
            name="Volume Discount",
            description="Progressive discount based on usage",
            active=True,
        )

        # Create discount tiers
        # 0-9 units: 0% discount
        DiscountTier.objects.create(
            discount_model=self.discount_model,
            min_units=0,
            max_units=10,
            discount_percent=Decimal("0.00"),
        )

        # 10-49 units: 10% discount
        DiscountTier.objects.create(
            discount_model=self.discount_model,
            min_units=10,
            max_units=50,
            discount_percent=Decimal("10.00"),
        )

        # 50+ units: 20% discount
        DiscountTier.objects.create(
            discount_model=self.discount_model,
            min_units=50,
            max_units=None,  # Unlimited
            discount_percent=Decimal("20.00"),
        )

    def test_discount_model_str_representation(self):
        """Test string representation of DiscountModel"""
        self.assertEqual(str(self.discount_model), "Volume Discount")

    def test_calculate_discount_no_discount_tier(self):
        """Test discount calculation for units in first tier (no discount)"""
        base_rate = Decimal("10.00")
        units = 5

        final_price = self.discount_model.calculate_discount(base_rate, units)
        expected_price = base_rate * units  # No discount

        self.assertEqual(final_price, expected_price)

    def test_calculate_discount_single_tier(self):
        """Test discount calculation for units in second tier"""
        base_rate = Decimal("10.00")
        units = 15

        final_price = self.discount_model.calculate_discount(base_rate, units)

        # First 10 units at full price, next 5 at 90% (10% discount)
        expected_price = (base_rate * 10) + (base_rate * Decimal("0.9") * 5)

        self.assertEqual(final_price, expected_price)

    def test_calculate_discount_multiple_tiers(self):
        """Test discount calculation spanning multiple tiers"""
        base_rate = Decimal("10.00")
        units = 60

        final_price = self.discount_model.calculate_discount(base_rate, units)

        # First 10 units at 100% (0% discount)
        # Next 40 units at 90% (10% discount)
        # Next 10 units at 80% (20% discount)
        expected_price = (
            (base_rate * 10)
            + (base_rate * Decimal("0.9") * 40)
            + (base_rate * Decimal("0.8") * 10)
        )

        self.assertEqual(final_price, expected_price)

    def test_get_discount_breakdown(self):
        """Test discount breakdown calculation"""
        base_rate = Decimal("10.00")
        units = 25

        breakdown = self.discount_model.get_discount_breakdown(base_rate, units)

        self.assertEqual(len(breakdown), 2)  # Should have 2 tiers

        # First tier: 0-9 units at 0% discount
        self.assertEqual(breakdown[0]["units"], 10)
        self.assertEqual(breakdown[0]["discount_percent"], Decimal("0.00"))
        self.assertEqual(breakdown[0]["rate"], base_rate)

        # Second tier: 10-24 units at 10% discount
        self.assertEqual(breakdown[1]["units"], 15)
        self.assertEqual(breakdown[1]["discount_percent"], Decimal("10.00"))
        self.assertEqual(breakdown[1]["rate"], base_rate * Decimal("0.9"))

    def test_discount_tier_str_representation(self):
        """Test string representation of DiscountTier"""
        tier = self.discount_model.tiers.get(min_units=10, max_units=50)
        expected_str = "Volume Discount: 10-49 units → 10.00% discount"
        self.assertEqual(str(tier), expected_str)

        # Test unlimited tier
        tier_unlimited = self.discount_model.tiers.get(min_units=50, max_units=None)
        expected_str = "Volume Discount: 50+ units → 20.00% discount"
        self.assertEqual(str(tier_unlimited), expected_str)


class VSHNAppCatPriceTestCase(TestCase):
    """Test cases for VSHNAppCatPrice model and final price calculations"""

    def setUp(self):
        # Create test service
        self.service = Service.objects.create(
            name="PostgreSQL",
            slug="postgresql",
            description="PostgreSQL database service",
            features="High availability, automated backups",
        )

        # Create discount model
        self.discount_model = ProgressiveDiscountModel.objects.create(
            name="RAM Discount",
            description="Progressive discount for RAM usage",
            active=True,
        )

        # Create discount tiers
        DiscountTier.objects.create(
            discount_model=self.discount_model,
            min_units=0,
            max_units=5,
            discount_percent=Decimal("0.00"),
        )

        DiscountTier.objects.create(
            discount_model=self.discount_model,
            min_units=5,
            max_units=None,
            discount_percent=Decimal("10.00"),
        )

        # Create price configuration
        self.price_config = VSHNAppCatPrice.objects.create(
            service=self.service,
            variable_unit=VSHNAppCatPrice.VariableUnit.RAM,
            term=Term.MTH,
            discount_model=self.discount_model,
        )

    def test_vshn_appcat_price_str_representation(self):
        """Test string representation of VSHNAppCatPrice"""
        expected_str = "PostgreSQL - Memory (RAM) based pricing"
        self.assertEqual(str(self.price_config), expected_str)

    def test_base_fee_creation_and_retrieval(self):
        """Test creation and retrieval of base fees"""
        base_fee = VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        retrieved_fee = self.price_config.get_base_fee(Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED)
        self.assertEqual(retrieved_fee, Decimal("50.00"))

        # Test non-existent currency
        non_existent_fee = self.price_config.get_base_fee(Currency.EUR, VSHNAppCatPrice.ServiceLevel.GUARANTEED)
        self.assertIsNone(non_existent_fee)

    def test_unit_rate_creation_and_retrieval(self):
        """Test creation and retrieval of unit rates"""
        unit_rate = VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

        retrieved_rate = self.price_config.get_unit_rate(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED
        )
        self.assertEqual(retrieved_rate, Decimal("5.0000"))

        # Test non-existent combination
        non_existent_rate = self.price_config.get_unit_rate(
            Currency.EUR, VSHNAppCatPrice.ServiceLevel.GUARANTEED
        )
        self.assertIsNone(non_existent_rate)

    def test_calculate_final_price_simple(self):
        """Test final price calculation without addons"""
        # Create base fee and unit rate
        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

        # Calculate price for 3 GiB RAM (should be in first tier, no discount)
        result = self.price_config.calculate_final_price(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, 3
        )

        expected_total = Decimal("50.00") + (Decimal("5.0000") * 3)
        self.assertEqual(result["total_price"], expected_total)
        self.assertEqual(result["addon_total"], 0)

    def test_calculate_final_price_with_discount(self):
        """Test final price calculation with progressive discount"""
        # Create base fee and unit rate
        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

        # Calculate price for 8 GiB RAM (spans two discount tiers)
        result = self.price_config.calculate_final_price(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, 8
        )

        # First 5 units at full price, next 3 at 90% (10% discount)
        discounted_price = (Decimal("5.0000") * 5) + (
            Decimal("5.0000") * Decimal("0.9") * 3
        )
        expected_total = Decimal("50.00") + discounted_price

        self.assertEqual(result["total_price"], expected_total)

    def test_calculate_final_price_negative_units(self):
        """Test final price calculation with negative units (should raise error)"""
        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

        with self.assertRaises(ValueError):
            self.price_config.calculate_final_price(
                Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, -1
            )

    def test_calculate_final_price_missing_data(self):
        """Test final price calculation when base fee or unit rate is missing"""
        # Only create base fee, no unit rate
        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        result = self.price_config.calculate_final_price(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, 3
        )

        self.assertIsNone(result)

    def test_calculate_final_price_without_discount_model(self):
        """Test final price calculation when no discount model is active"""
        # Remove discount model
        self.price_config.discount_model = None
        self.price_config.save()

        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

        result = self.price_config.calculate_final_price(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, 8
        )

        # Should be simple multiplication without discount
        expected_total = Decimal("50.00") + (Decimal("5.0000") * 8)
        self.assertEqual(result["total_price"], expected_total)


class VSHNAppCatAddonTestCase(TestCase):
    """Test cases for VSHNAppCat addons and pricing"""

    def setUp(self):
        # Create test service
        self.service = Service.objects.create(
            name="PostgreSQL",
            slug="postgresql",
            description="PostgreSQL database service",
            features="High availability, automated backups",
        )

        # Create price configuration
        self.price_config = VSHNAppCatPrice.objects.create(
            service=self.service,
            variable_unit=VSHNAppCatPrice.VariableUnit.RAM,
            term=Term.MTH,
        )

        # Create base fee and unit rate for main service
        VSHNAppCatBaseFee.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("50.00"),
        )

        VSHNAppCatUnitRate.objects.create(
            vshn_appcat_price_config=self.price_config,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("5.0000"),
        )

    def test_addon_base_fee_type(self):
        """Test addon with base fee type"""
        addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Backup Extension",
            description="Extended backup retention",
            addon_type=VSHNAppCatAddon.AddonType.BASE_FEE,
            mandatory=True,
        )

        # Create base fee for addon
        VSHNAppCatAddonBaseFee.objects.create(
            addon=addon, currency=Currency.CHF, service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED, amount=Decimal("25.00")
        )

        # Test get_price method
        price = addon.get_price(Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED)
        self.assertEqual(price, Decimal("25.00"))

    def test_addon_unit_rate_type(self):
        """Test addon with unit rate type"""
        addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Additional Storage",
            description="Extra storage per GiB",
            addon_type=VSHNAppCatAddon.AddonType.UNIT_RATE,
            mandatory=False,
        )

        # Create unit rate for addon
        VSHNAppCatAddonUnitRate.objects.create(
            addon=addon,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("0.5000"),
        )

        # Test get_price method
        price = addon.get_price(Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED)
        self.assertEqual(price, Decimal("0.5000"))

    def test_addon_unit_rate_without_service_level(self):
        """Test addon unit rate without service level (should raise error)"""
        addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Additional Storage",
            addon_type=VSHNAppCatAddon.AddonType.UNIT_RATE,
        )

        VSHNAppCatAddonUnitRate.objects.create(
            addon=addon,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("0.5000"),
        )

        with self.assertRaises(ValueError):
            addon.get_price(Currency.CHF)

    def test_calculate_final_price_with_mandatory_addons(self):
        """Test final price calculation including mandatory addons"""
        # Create mandatory base fee addon
        mandatory_addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Backup Extension",
            addon_type=VSHNAppCatAddon.AddonType.BASE_FEE,
            mandatory=True,
        )

        VSHNAppCatAddonBaseFee.objects.create(
            addon=mandatory_addon, currency=Currency.CHF, service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED, amount=Decimal("25.00")
        )

        # Create mandatory unit rate addon
        mandatory_unit_addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Monitoring",
            addon_type=VSHNAppCatAddon.AddonType.UNIT_RATE,
            mandatory=True,
        )

        VSHNAppCatAddonUnitRate.objects.create(
            addon=mandatory_unit_addon,
            currency=Currency.CHF,
            service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            amount=Decimal("1.0000"),
        )

        result = self.price_config.calculate_final_price(
            Currency.CHF, VSHNAppCatPrice.ServiceLevel.GUARANTEED, 4
        )

        # Base: 50 + (5 * 4) = 70
        # Mandatory addons: 25 + (1 * 4) = 29
        # Total: 99
        expected_total = Decimal("99.00")
        self.assertEqual(result["total_price"], expected_total)
        self.assertEqual(result["addon_total"], Decimal("29.00"))
        self.assertEqual(len(result["addon_breakdown"]), 2)

    def test_calculate_final_price_with_selected_addons(self):
        """Test final price calculation including selected optional addons"""
        # Create optional addon
        optional_addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="SSL Certificate",
            addon_type=VSHNAppCatAddon.AddonType.BASE_FEE,
            mandatory=False,
        )

        VSHNAppCatAddonBaseFee.objects.create(
            addon=optional_addon, currency=Currency.CHF, service_level=VSHNAppCatPrice.ServiceLevel.GUARANTEED, amount=Decimal("15.00")
        )

        # Calculate price with selected addon
        result = self.price_config.calculate_final_price(
            Currency.CHF,
            VSHNAppCatPrice.ServiceLevel.GUARANTEED,
            4,
            addon_ids=[optional_addon.id],
        )

        # Base: 50 + (5 * 4) = 70
        # Selected addon: 15
        # Total: 85
        expected_total = Decimal("85.00")
        self.assertEqual(result["total_price"], expected_total)
        self.assertEqual(result["addon_total"], Decimal("15.00"))

    def test_addon_str_representation(self):
        """Test string representation of addon"""
        addon = VSHNAppCatAddon.objects.create(
            vshn_appcat_price_config=self.price_config,
            name="Backup Extension",
            addon_type=VSHNAppCatAddon.AddonType.BASE_FEE,
        )

        expected_str = "PostgreSQL - Backup Extension"
        self.assertEqual(str(addon), expected_str)
