"""
Unit tests for FreeExile Savage Barter Economy, Salvage Yields,
Feral Corruption Item Sinks, and Inflation Monitoring.
"""

import unittest
from server.economy.economy_types import SavageCurrencyType, CorruptionResultType
from server.economy.salvage_engine import SalvageEngine
from server.economy.corruption_engine import FeralCorruptionEngine
from server.economy.inflation_monitor import InflationMonitor
from server.world.primal_stones_crafting import GearItem, ItemRarity, Affix, AffixType


class TestEconomyMechanics(unittest.TestCase):
    def setUp(self):
        self.salvage_engine = SalvageEngine()
        self.corruption_engine = FeralCorruptionEngine(rng_seed=42)
        self.inflation_monitor = InflationMonitor(market_tax_rate=0.05)

    def test_salvage_yield_and_bloodstone_assembly(self):
        # 1. Common White Item
        white_item = GearItem(
            item_uuid="white_1",
            item_name="Thiết Đao Rỉ Sét",
            item_level=10,
            rarity=ItemRarity.PHAM_PHAM
        )
        res1, dust1, stones1 = self.salvage_engine.process_salvage(white_item, current_player_dust=0)
        self.assertEqual(res1.dust_shards, 2)
        self.assertEqual(dust1, 2)
        self.assertEqual(stones1, 0)

        # 2. Endgame Rare Item (iLvl 85)
        rare_item = GearItem(
            item_uuid="rare_1",
            item_name="Cổ Kiếm Huyết Sát",
            item_level=86,
            rarity=ItemRarity.CUC_PHAM,
            affixes=[
                Affix("Phys", AffixType.PREFIX, "phys", 10, 50, 45),
                Affix("Crit", AffixType.SUFFIX, "crit", 5, 15, 12),
                Affix("Fire", AffixType.PREFIX, "fire", 10, 30, 25),
                Affix("Speed", AffixType.SUFFIX, "speed", 5, 20, 18)
            ]
        )
        # base_shards = (12 + 4*2) = 20 * 1.5 = 30 shards
        res2, dust2, stones2 = self.salvage_engine.process_salvage(rare_item, current_player_dust=dust1)
        self.assertEqual(res2.dust_shards, 30)
        # dust1 (2) + 30 = 32 dust -> 1 full Bloodstone awarded, 12 remaining dust
        self.assertEqual(stones2, 1)
        self.assertEqual(dust2, 12)
        self.assertIn("MANH_CO_COT_AN", res2.bonus_fragments)

    def test_feral_corruption_outcomes(self):
        # Create test item
        item = GearItem(
            item_uuid="corrupt_test_1",
            item_name="Huyết Lôi Ma Trượng",
            item_level=80,
            rarity=ItemRarity.CUC_PHAM,
            affixes=[Affix("Lôi Sát", AffixType.PREFIX, "lightning", 20, 50, 35)]
        )

        # Already corrupted check
        outcome_rejected, _ = self.corruption_engine.corrupt_item(item, is_already_corrupted=True)
        self.assertFalse(outcome_rejected.success)
        self.assertIn("không thể biến dị lần hai", outcome_rejected.message)

        # Run deterministically with known seed
        # Seed 42:
        engine = FeralCorruptionEngine(rng_seed=100)
        # Roll several iterations to verify valid outcomes
        outcomes_seen = set()
        for i in range(20):
            test_item = GearItem(
                item_uuid=f"item_{i}",
                item_name=f"Tà Binh #{i}",
                item_level=80,
                rarity=ItemRarity.CUC_PHAM,
                affixes=[Affix("Sát Thương", AffixType.PREFIX, "dmg", 10, 50, 20)]
            )
            outcome, resulting_item = engine.corrupt_item(test_item)
            outcomes_seen.add(outcome.result_type)
            if outcome.result_type == CorruptionResultType.PERMANENT_DESTROY:
                self.assertTrue(outcome.item_destroyed)
                self.assertIsNone(resulting_item)
            elif outcome.result_type == CorruptionResultType.GOD_TIER_IMPLICIT:
                self.assertIsNotNone(outcome.implicit_added)
                self.assertTrue(resulting_item.affixes[0].name.startswith("[Dị Biến Tà Huyết]"))

        # Verify destruction and godly implicits both occur
        self.assertIn(CorruptionResultType.PERMANENT_DESTROY, outcomes_seen)
        self.assertIn(CorruptionResultType.GOD_TIER_IMPLICIT, outcomes_seen)

    def test_inflation_monitor_sii_and_elasticity(self):
        # Generate 1000 Huyết Hồn Thạch (faucets)
        self.inflation_monitor.record_faucet("HUYET_HON_THACH", 1000)

        # Crafting burned 600 Huyết Hồn Thạch
        self.inflation_monitor.record_crafting_sink("HUYET_HON_THACH", 600)

        # Market trades of 4000 stones with 5% tax = 200 stones burned
        tax_burned = self.inflation_monitor.record_market_trade_tax_burn("HUYET_HON_THACH", 4000)
        self.assertEqual(tax_burned, 200)

        # Total sinks = 600 + 200 = 800. Faucets = 1000.
        # SII = 1000 / 800 = 1.25
        sii = self.inflation_monitor.calculate_server_inflation_index("HUYET_HON_THACH")
        self.assertEqual(sii, 1.25)

        # In mild inflation zone (1.25), drop modifier should be slightly dampened to protect currency value
        drop_mod = self.inflation_monitor.get_dynamic_drop_modifier("HUYET_HON_THACH")
        self.assertEqual(drop_mod, 0.92)

        # Add more craft burns (400 more) -> total sinks = 1200, SII = 1000 / 1200 = 0.833
        self.inflation_monitor.record_crafting_sink("HUYET_HON_THACH", 400)
        new_sii = self.inflation_monitor.calculate_server_inflation_index("HUYET_HON_THACH")
        self.assertAlmostEqual(new_sii, 0.833, places=2)
        new_drop_mod = self.inflation_monitor.get_dynamic_drop_modifier("HUYET_HON_THACH")
        self.assertEqual(new_drop_mod, 1.0)

        # Check dashboard report
        report = self.inflation_monitor.get_economy_dashboard_report()
        self.assertEqual(report["primary_currency"], "HUYET_HON_THACH")
        self.assertIn("total_faucets", report)
        self.assertIn("total_market_tax_burns", report)


if __name__ == "__main__":
    unittest.main()
