"""
Unit tests for FreeExile Character Ascendancy & Flesh Metamorphosis System.
Tests:
- 10 Primal Trials progression (3 points each, 30 points total).
- Ascendancy Class Selection & Tree Validation.
- Minor (1pt), Notable (2pt), and Keystone (3pt) allocation & prerequisite graph.
- Rule-Bending Paradigm Shifts for Items, Skills, and Base Stats.
"""

import os
import sys
import unittest

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../server")))

from server.world.ascendancy_types import (
    AscendancyClassId,
    AscendancyNodeType,
    KeystoneParadigm,
)
from server.world.ascendancy_catalog import (
    get_canonical_trials,
    get_ascendancy_nodes,
)
from server.world.ascendancy_engine import AscendancyEngine
from server.world.martial_types import CharacterLoadout, MartialSect


class TestAscendancySystem(unittest.TestCase):
    def setUp(self):
        self.engine = AscendancyEngine()
        self.actor_id = 999
        self.loadout = CharacterLoadout(
            actor_id=self.actor_id,
            name="ManTocLuuDay",
            sect=MartialSect.THANH_PHONG,
            cuong_the=50,
            than_phap=50,
            than_niem=50,
            max_hp=1000.0,
            max_qi=500.0,
            max_ward_barrier=200.0,
        )

    def test_canonical_trials_count_and_rewards(self):
        """Kiểm tra có đúng 10 Thí Luyện Cổ Đàn, mỗi lần thưởng đúng 3 điểm."""
        trials = get_canonical_trials()
        self.assertEqual(len(trials), 10)
        total_points = sum(t.reward_points for t in trials)
        self.assertEqual(total_points, 30)

        # Kiểm tra level tăng dần từ 35 đến 100
        self.assertEqual(trials[0].required_level, 35)
        self.assertEqual(trials[-1].required_level, 100)

    def test_sequential_trial_completion_awards_30_points(self):
        """Hoàn thành lần lượt 10 thí luyện, nhận đủ 30 điểm thăng hoa."""
        state = self.engine.get_or_create_state(self.actor_id)
        self.assertEqual(state.total_points_earned, 0)

        # Thử hoàn thành thí luyện 2 khi chưa làm thí luyện 1 -> Thất bại
        ok, msg, pts = self.engine.complete_trial(self.actor_id, trial_index=2)
        self.assertFalse(ok)
        self.assertEqual(pts, 0)

        # Hoàn thành tuần tự từ 1 đến 10
        for i in range(1, 11):
            ok, msg, pts = self.engine.complete_trial(self.actor_id, trial_index=i)
            self.assertTrue(ok)
            self.assertEqual(pts, 3)
            self.assertEqual(state.total_points_earned, i * 3)

        self.assertEqual(state.total_points_earned, 30)
        self.assertEqual(len(state.completed_trials), 10)

        # Không thể lặp lại thí luyện đã hoàn thành
        ok_dup, _, _ = self.engine.complete_trial(self.actor_id, trial_index=1)
        self.assertFalse(ok_dup)

    def test_class_selection_and_prerequisite_rules(self):
        """Kiểm tra chọn Hệ Phái Thăng Hoa và quy tắc mắt xích dẫn đường."""
        # Chưa làm thí luyện nào -> Không thể chọn Hệ Phái
        ok, msg = self.engine.choose_ascendancy_class(
            self.actor_id, AscendancyClassId.TUYET_MENH_KIEM_MA
        )
        self.assertFalse(ok)

        # Hoàn thành Thí Luyện 1 -> Mở khóa chọn Hệ Phái
        self.engine.complete_trial(self.actor_id, 1)
        ok, msg = self.engine.choose_ascendancy_class(
            self.actor_id, AscendancyClassId.TUYET_MENH_KIEM_MA
        )
        self.assertTrue(ok)

        # Thử cộng node cấp cao (103 Notable) khi chưa cộng node tiên quyết (101, 102)
        ok_notable, msg_notable = self.engine.allocate_node(self.actor_id, 103)
        self.assertFalse(ok_notable)
        self.assertIn("tiên quyết", msg_notable)

        # Cộng tuần tự: 101 (Minor: 1 pt) -> 102 (Minor: 1 pt) -> 103 (Notable: 2 pt)
        ok_101, _ = self.engine.allocate_node(self.actor_id, 101)
        self.assertTrue(ok_101)

        ok_102, _ = self.engine.allocate_node(self.actor_id, 102)
        self.assertTrue(ok_102)

        # Hiện tại có 3 điểm (Thí luyện 1), đã dùng 2 điểm cho 101 và 102, còn 1 điểm.
        # Node 103 tốn 2 điểm -> Không đủ điểm
        ok_103, msg_fail = self.engine.allocate_node(self.actor_id, 103)
        self.assertFalse(ok_103)
        self.assertIn("Không đủ điểm", msg_fail)

        # Hoàn thành Thí Luyện 2 (nhận thêm 3 điểm = tổng 6 điểm, còn 4 điểm)
        self.engine.complete_trial(self.actor_id, 2)
        ok_103_success, _ = self.engine.allocate_node(self.actor_id, 103)
        self.assertTrue(ok_103_success)

    def test_item_mutation_keystone(self):
        """Kiểm tra Keystone biến đổi ITEM: Độc Binh Ma Cốt (Khóa offhand, 2H +150%, Chuyển Chaos)."""
        # Hoàn thành đủ 3 thí luyện (9 điểm)
        for i in range(1, 4):
            self.engine.complete_trial(self.actor_id, i)

        self.engine.choose_ascendancy_class(self.actor_id, AscendancyClassId.TUYET_MENH_KIEM_MA)
        self.engine.allocate_node(self.actor_id, 101) # Minor (1)
        self.engine.allocate_node(self.actor_id, 102) # Minor (1)
        self.engine.allocate_node(self.actor_id, 103) # Notable (2)
        self.engine.allocate_node(self.actor_id, 104) # Keystone Độc Binh Ma Cốt (3)

        effects = self.engine.calculate_mutations(self.actor_id, self.loadout)
        self.assertTrue(effects.offhand_disabled)
        self.assertEqual(effects.two_hand_stat_boost, 2.5) # +150%
        self.assertTrue(effects.skills_convert_to_chaos)
        self.assertIn("RULE_SINGLE_WEAPON_CHAOS", effects.active_paradigm_rules)

    def test_skill_transformation_keystone(self):
        """Kiểm tra Keystone biến đổi SKILL: Huyết Tế Thần Công (Dùng máu thay mana, +50% uy lực)."""
        for i in range(1, 4):
            self.engine.complete_trial(self.actor_id, i)

        self.engine.choose_ascendancy_class(self.actor_id, AscendancyClassId.HUYET_TUY_MA_Y)
        self.engine.allocate_node(self.actor_id, 401) # Minor (1)
        self.engine.allocate_node(self.actor_id, 402) # Notable (2)
        self.engine.allocate_node(self.actor_id, 403) # Keystone Huyết Tế Thần Công (3)

        effects = self.engine.calculate_mutations(self.actor_id, self.loadout)
        self.assertTrue(effects.skills_cost_life)
        self.assertAlmostEqual(effects.skills_damage_boost, 1.50)
        self.assertIn("RULE_BLOOD_PRIMACY", effects.active_paradigm_rules)

    def test_base_stat_mutation_keystones(self):
        """Kiểm tra Keystone biến đổi CHỈ SỐ: Cốt Giáp Đoạt Mệnh (HP cố định 1, Ward x5, Miễn nhiễm Chaos/Độc)."""
        for i in range(1, 4):
            self.engine.complete_trial(self.actor_id, i)

        self.engine.choose_ascendancy_class(self.actor_id, AscendancyClassId.BANG_CUC_MA_QUAN)
        self.engine.allocate_node(self.actor_id, 501) # Minor (1)
        self.engine.allocate_node(self.actor_id, 502) # Notable (2)
        self.engine.allocate_node(self.actor_id, 503) # Keystone Cốt Giáp Đoạt Mệnh (3)

        effects = self.engine.calculate_mutations(self.actor_id, self.loadout)
        self.assertEqual(effects.forced_max_hp, 1.0)
        self.assertAlmostEqual(effects.ward_multiplier, 5.0)
        self.assertTrue(effects.chaos_immune)
        self.assertTrue(effects.poison_immune)
        self.assertIn("RULE_CHAOS_INOCULATION_BONE", effects.active_paradigm_rules)


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