"""
Unit Test Suite for FreeExile Quest & Milestone Engine.
Validates:
- Normal quests registration, acceptance, progression, and completion.
- Hidden quests triggered by cryptic environmental & combat criteria.
- High-tier epic quests with demanding multi-objective prerequisites.
- Milestone tracking, unlock progression, and anti-double claim validation.
- Zero-trust server authority validations.
"""

import pytest
from server.world.quest_engine import (
    QuestEngine,
    QuestType,
    QuestStatus,
    ObjectiveType,
    HiddenTriggerType,
    ObjectiveState,
    RewardItem,
    QuestReward,
    HiddenTrigger,
    QuestDefinition,
    MilestoneTier,
)


@pytest.fixture
def quest_engine() -> QuestEngine:
    engine = QuestEngine()

    # 1. Normal Quests
    engine.register_quest(
        QuestDefinition(
            quest_id="norm_tang_kiem_nhai",
            title="Tỉnh Giấc Tại Táng Kiếm Nhai",
            description="Tiêu diệt Hoang Giới Thi Cẩu đang vây quanh tàn tích.",
            quest_type=QuestType.NORMAL,
            required_level=1,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_kill_bone_hounds",
                    description="Tiêu diệt Hoang Giới Thi Cẩu",
                    obj_type=ObjectiveType.KILL_MONSTER,
                    target_id="mob_trash_bone_hound",
                    target_count=5,
                )
            ],
            reward=QuestReward(
                exp=500,
                spirit_stones=[RewardItem("currency_awakening_pearl", "Khai Linh Châu", 3)],
                meridian_points=1,
            ),
        )
    )

    engine.register_quest(
        QuestDefinition(
            quest_id="norm_khai_thong_dan_tian",
            title="Thức Tỉnh Đan Điền",
            description="Khai mở huyệt vị Đan Điền đầu tiên trên Kỳ Kinh Bát Mạch.",
            quest_type=QuestType.NORMAL,
            required_level=2,
            prerequisite_quest_ids=["norm_tang_kiem_nhai"],
            objectives=[
                ObjectiveState(
                    objective_id="obj_unlock_dantian",
                    description="Khai mở Đan Điền",
                    obj_type=ObjectiveType.ACTIVATE_MERIDIAN,
                    target_id="meridian_origin_dantian",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=1000,
                spirit_stones=[RewardItem("currency_creation_stone", "Tạo Hóa Thạch", 2)],
                meridian_points=2,
            ),
        )
    )

    # 2. Hidden Quests
    engine.register_quest(
        QuestDefinition(
            quest_id="hidden_huyen_anh_to_su",
            title="Kỳ Ngộ: Bí Thất Huyễn Ảnh",
            description="Lướt Huyễn Ảnh Bộ trong trạng thái thập tử nhất sinh tại Cổ Trận Huyết Lân.",
            quest_type=QuestType.HIDDEN,
            required_level=5,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_reach_secret_shrine",
                    description="Khám phá mật điện Huyễn Ảnh Tông",
                    obj_type=ObjectiveType.DISCOVER_LOCATION,
                    target_id="loc_secret_phantom_shrine",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=3500,
                spirit_stones=[
                    RewardItem("currency_primal_chaos_stone", "Hỗn Nguyên Thạch", 5),
                    RewardItem("core_chan_vo_dan", "Chân Võ Đan: Huyễn Ảnh Bộ Thượng Thừa", 1),
                ],
                meridian_points=3,
                title="Huyễn Ảnh Ẩn Hành",
            ),
            hidden_trigger=HiddenTrigger(
                trigger_type=HiddenTriggerType.EVASION_NEAR_DEATH,
                required_zone="zone_blood_scale_ruins",
                required_hp_percent=0.10,
                description="Né tránh hoàn hảo khi sinh mệnh dưới 10% tại Cổ Trận Huyết Lân.",
            ),
        )
    )

    engine.register_quest(
        QuestDefinition(
            quest_id="hidden_tan_bi_than_tuong",
            title="Kỳ Ngộ: Tàn Bi Thần Tướng",
            description="Hiến tế Tạo Hóa Thạch trước Tàn Bi Thần Tướng giữa bão cát hoang mạc.",
            quest_type=QuestType.HIDDEN,
            required_level=10,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_decipher_stele",
                    description="Giải mã Thần Tướng Bi Văn",
                    obj_type=ObjectiveType.PERFORM_ACTION,
                    target_id="action_decipher_ancient_stele",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=5000,
                spirit_stones=[
                    RewardItem("currency_sovereign_sigil", "Thần Tướng Phù", 2),
                    RewardItem("currency_primal_chaos_stone", "Hỗn Nguyên Thạch", 10),
                ],
                meridian_points=4,
                title="Thần Tướng Thừa Kế",
            ),
            hidden_trigger=HiddenTrigger(
                trigger_type=HiddenTriggerType.RELIC_OFFERING,
                required_zone="zone_boundless_sandstorm",
                required_item_id="currency_creation_stone",
                description="Tế Tạo Hóa Thạch lên Bia Đá Vô Danh trong bão cát.",
            ),
        )
    )

    # 3. High-Tier / Epic Quests
    engine.register_quest(
        QuestDefinition(
            quest_id="epic_phong_ma_co_tran",
            title="Đại Chiến Phong Ma Cổ Trận",
            description="Thanh tẩy Cổ Trận, trảm Hoàng Kim Yêu Thú và đột phá Nghịch Mệnh Đại Huyệt.",
            quest_type=QuestType.HIGH_TIER,
            required_level=20,
            prerequisite_quest_ids=["norm_khai_thong_dan_tian"],
            objectives=[
                ObjectiveState(
                    objective_id="obj_kill_golden_boss",
                    description="Trảm Hoàng Kim Yêu Thú: Hắc Long Thần Ma",
                    obj_type=ObjectiveType.KILL_MONSTER,
                    target_id="boss_black_dragon_demon",
                    target_count=1,
                ),
                ObjectiveState(
                    objective_id="obj_breakthrough_keystone",
                    description="Đột phá Nghịch Mệnh Đại Huyệt: Bất Hoại Thần Thể",
                    obj_type=ObjectiveType.ACTIVATE_MERIDIAN,
                    target_id="meridian_keystone_unbreakable",
                    target_count=1,
                ),
            ],
            reward=QuestReward(
                exp=50000,
                spirit_stones=[
                    RewardItem("currency_void_mirror_relic", "Thái Hư Càn Khôn Kính", 1),
                    RewardItem("currency_destiny_rune", "Thiên Mệnh Đan", 5),
                    RewardItem("currency_sovereign_sigil", "Thần Tướng Phù", 3),
                ],
                meridian_points=10,
                title="Nghịch Mệnh Tiên Phong",
            ),
        )
    )

    # 4. Milestone Tiers
    engine.register_milestone(
        MilestoneTier(
            milestone_id="ms_tier_1_so_nhap",
            title="Mốc I: Sơ Nhập Hoang Giới",
            description="Hoàn thành tối thiểu 1 nhiệm vụ.",
            required_total_completed=1,
            required_hidden_completed=0,
            required_high_tier_completed=0,
            reward=QuestReward(
                exp=1000,
                spirit_stones=[RewardItem("currency_awakening_pearl", "Khai Linh Châu", 5)],
                meridian_points=1,
            ),
        )
    )

    engine.register_milestone(
        MilestoneTier(
            milestone_id="ms_tier_2_thong_kinh",
            title="Mốc II: Thông Suốt Kinh Mạch",
            description="Hoàn thành 2 nhiệm vụ thường và 1 nhiệm vụ ẩn.",
            required_total_completed=3,
            required_hidden_completed=1,
            required_high_tier_completed=0,
            reward=QuestReward(
                exp=5000,
                spirit_stones=[
                    RewardItem("currency_primal_chaos_stone", "Hỗn Nguyên Thạch", 5),
                    RewardItem("currency_destiny_rune", "Thiên Mệnh Đan", 1),
                ],
                meridian_points=3,
                title="Thông Mạch Tông Sư",
            ),
        )
    )

    engine.register_milestone(
        MilestoneTier(
            milestone_id="ms_tier_3_chi_ton",
            title="Mốc III: Vạn Giới Chí Tôn",
            description="Hoàn thành 3 nhiệm vụ tổng thể, 1 nhiệm vụ ẩn và 1 nhiệm vụ cấp cao.",
            required_total_completed=3,
            required_hidden_completed=1,
            required_high_tier_completed=1,
            reward=QuestReward(
                exp=100000,
                spirit_stones=[
                    RewardItem("currency_void_mirror_relic", "Thái Hư Càn Khôn Kính", 2),
                    RewardItem("currency_destiny_rune", "Thiên Mệnh Đan", 10),
                ],
                meridian_points=12,
                title="Vạn Giới Chí Tôn",
            ),
        )
    )

    return engine


class TestQuestEngine:
    def test_normal_quest_lifecycle(self, quest_engine: QuestEngine):
        player_id = "player_exile_001"
        quest_engine.initialize_player(player_id, player_level=1)

        # 1. Available quests
        available = quest_engine.get_available_quests(player_id, player_level=1)
        assert len(available) == 1
        assert available[0].quest_id == "norm_tang_kiem_nhai"

        # 2. Cannot accept quest with missing prerequisites or level
        assert not quest_engine.accept_quest(player_id, "norm_khai_thong_dan_tian", player_level=1)

        # 3. Accept first quest
        assert quest_engine.accept_quest(player_id, "norm_tang_kiem_nhai", player_level=1)
        quest = quest_engine.get_player_quest(player_id, "norm_tang_kiem_nhai")
        assert quest is not None
        assert quest.status == QuestStatus.IN_PROGRESS

        # 4. Progress kill objective
        updated = quest_engine.record_kill(player_id, "mob_trash_bone_hound", count=3)
        assert "norm_tang_kiem_nhai" in updated
        quest = quest_engine.get_player_quest(player_id, "norm_tang_kiem_nhai")
        assert quest.status == QuestStatus.IN_PROGRESS

        # Complete kills
        updated = quest_engine.record_kill(player_id, "mob_trash_bone_hound", count=2)
        assert "norm_tang_kiem_nhai" in updated
        quest = quest_engine.get_player_quest(player_id, "norm_tang_kiem_nhai")
        assert quest.status == QuestStatus.COMPLETED

        # 5. Claim reward
        success, reward, msg = quest_engine.claim_quest_reward(player_id, "norm_tang_kiem_nhai")
        assert success is True
        assert reward is not None
        assert reward.exp == 500
        assert reward.meridian_points == 1
        assert quest.status == QuestStatus.CLAIMED

        # 6. Anti-double claim
        success2, _, msg2 = quest_engine.claim_quest_reward(player_id, "norm_tang_kiem_nhai")
        assert success2 is False
        assert "đã nhận" in msg2.lower() or "already claimed" in msg2.lower()

    def test_prerequisite_chain_unlocking(self, quest_engine: QuestEngine):
        player_id = "player_exile_002"
        quest_engine.initialize_player(player_id, player_level=2)

        # Accept and complete quest 1
        quest_engine.accept_quest(player_id, "norm_tang_kiem_nhai", player_level=2)
        quest_engine.record_kill(player_id, "mob_trash_bone_hound", count=5)
        quest_engine.claim_quest_reward(player_id, "norm_tang_kiem_nhai")

        # Now quest 2 is available
        available = quest_engine.get_available_quests(player_id, player_level=2)
        avail_ids = [q.quest_id for q in available]
        assert "norm_khai_thong_dan_tian" in avail_ids

        # Accept quest 2 and unlock meridian
        assert quest_engine.accept_quest(player_id, "norm_khai_thong_dan_tian", player_level=2)
        quest_engine.record_meridian_unlock(player_id, "meridian_origin_dantian")
        q2 = quest_engine.get_player_quest(player_id, "norm_khai_thong_dan_tian")
        assert q2.status == QuestStatus.COMPLETED

    def test_hidden_quest_trigger_mechanisms(self, quest_engine: QuestEngine):
        player_id = "player_exile_003"
        quest_engine.initialize_player(player_id, player_level=5)

        # Hidden quest should NOT be in standard available quests
        avail = quest_engine.get_available_quests(player_id, player_level=5)
        avail_ids = [q.quest_id for q in avail]
        assert "hidden_huyen_anh_to_su" not in avail_ids

        # Fails trigger if HP is not low enough
        triggered = quest_engine.trigger_hidden_quest(
            player_id,
            HiddenTriggerType.EVASION_NEAR_DEATH,
            {"zone": "zone_blood_scale_ruins", "hp_percent": 0.50},
        )
        assert triggered is None

        # Fails trigger if wrong zone
        triggered = quest_engine.trigger_hidden_quest(
            player_id,
            HiddenTriggerType.EVASION_NEAR_DEATH,
            {"zone": "zone_other_place", "hp_percent": 0.05},
        )
        assert triggered is None

        # Succeeds when conditions are fully satisfied!
        triggered = quest_engine.trigger_hidden_quest(
            player_id,
            HiddenTriggerType.EVASION_NEAR_DEATH,
            {"zone": "zone_blood_scale_ruins", "hp_percent": 0.08},
        )
        assert triggered == "hidden_huyen_anh_to_su"

        # Now the hidden quest is actively in progress
        q_hidden = quest_engine.get_player_quest(player_id, "hidden_huyen_anh_to_su")
        assert q_hidden is not None
        assert q_hidden.status == QuestStatus.IN_PROGRESS

        # Complete hidden objective: discover secret shrine
        quest_engine.record_discovery(player_id, "loc_secret_phantom_shrine")
        assert q_hidden.status == QuestStatus.COMPLETED

        # Claim hidden reward
        ok, reward, _ = quest_engine.claim_quest_reward(player_id, "hidden_huyen_anh_to_su")
        assert ok is True
        assert reward.title == "Huyễn Ảnh Ẩn Hành"
        assert reward.meridian_points == 3

    def test_high_tier_epic_quest(self, quest_engine: QuestEngine):
        player_id = "player_exile_004"
        quest_engine.initialize_player(player_id, player_level=20)

        # Complete prerequisite quest first
        quest_engine.accept_quest(player_id, "norm_tang_kiem_nhai", 20)
        quest_engine.record_kill(player_id, "mob_trash_bone_hound", 5)
        quest_engine.claim_quest_reward(player_id, "norm_tang_kiem_nhai")

        quest_engine.accept_quest(player_id, "norm_khai_thong_dan_tian", 20)
        quest_engine.record_meridian_unlock(player_id, "meridian_origin_dantian")
        quest_engine.claim_quest_reward(player_id, "norm_khai_thong_dan_tian")

        # Now accept epic quest
        assert quest_engine.accept_quest(player_id, "epic_phong_ma_co_tran", 20)
        epic_q = quest_engine.get_player_quest(player_id, "epic_phong_ma_co_tran")
        assert epic_q.status == QuestStatus.IN_PROGRESS

        # Only kill boss: not completed yet
        quest_engine.record_kill(player_id, "boss_black_dragon_demon")
        assert epic_q.status == QuestStatus.IN_PROGRESS

        # Breakthrough keystone: completes both objectives
        quest_engine.record_meridian_unlock(player_id, "meridian_keystone_unbreakable")
        assert epic_q.status == QuestStatus.COMPLETED

        # Claim epic rewards
        ok, reward, _ = quest_engine.claim_quest_reward(player_id, "epic_phong_ma_co_tran")
        assert ok is True
        assert reward.meridian_points == 10
        assert reward.title == "Nghịch Mệnh Tiên Phong"
        # Check Void Mirror in reward
        mirror = next(i for i in reward.spirit_stones if i.item_id == "currency_void_mirror_relic")
        assert mirror.quantity == 1

    def test_milestone_tracking_and_rewards(self, quest_engine: QuestEngine):
        player_id = "player_exile_005"
        quest_engine.initialize_player(player_id, player_level=20)

        # Check initial milestones: none eligible
        ms_status = quest_engine.get_player_milestone_status(player_id)
        assert ms_status["total_completed"] == 0
        assert len(ms_status["eligible_milestones"]) == 0

        # Complete 1 normal quest
        quest_engine.accept_quest(player_id, "norm_tang_kiem_nhai", 20)
        quest_engine.record_kill(player_id, "mob_trash_bone_hound", 5)
        quest_engine.claim_quest_reward(player_id, "norm_tang_kiem_nhai")

        # Now Milestone I is eligible!
        ms_status = quest_engine.get_player_milestone_status(player_id)
        assert ms_status["total_completed"] == 1
        assert "ms_tier_1_so_nhap" in ms_status["eligible_milestones"]

        # Claim Milestone I
        ok, reward, msg = quest_engine.claim_milestone_reward(player_id, "ms_tier_1_so_nhap")
        assert ok is True
        assert reward is not None
        assert reward.meridian_points == 1

        # Anti-double claim on milestone
        ok2, _, msg2 = quest_engine.claim_milestone_reward(player_id, "ms_tier_1_so_nhap")
        assert ok2 is False
        assert "đã nhận" in msg2.lower() or "already claimed" in msg2.lower()

        # Cannot claim Milestone II yet (requires 3 total, 1 hidden)
        ok3, _, _ = quest_engine.claim_milestone_reward(player_id, "ms_tier_2_thong_kinh")
        assert ok3 is False

        # Trigger & complete hidden quest
        quest_engine.trigger_hidden_quest(
            player_id,
            HiddenTriggerType.EVASION_NEAR_DEATH,
            {"zone": "zone_blood_scale_ruins", "hp_percent": 0.05},
        )
        quest_engine.record_discovery(player_id, "loc_secret_phantom_shrine")
        quest_engine.claim_quest_reward(player_id, "hidden_huyen_anh_to_su")

        # Complete normal quest 2
        quest_engine.accept_quest(player_id, "norm_khai_thong_dan_tian", 20)
        quest_engine.record_meridian_unlock(player_id, "meridian_origin_dantian")
        quest_engine.claim_quest_reward(player_id, "norm_khai_thong_dan_tian")

        # Total completed = 3 (2 normal, 1 hidden). Milestone II is eligible!
        ms_status2 = quest_engine.get_player_milestone_status(player_id)
        assert ms_status2["total_completed"] == 3
        assert ms_status2["hidden_completed"] == 1
        assert "ms_tier_2_thong_kinh" in ms_status2["eligible_milestones"]

        ok4, reward4, _ = quest_engine.claim_milestone_reward(player_id, "ms_tier_2_thong_kinh")
        assert ok4 is True
        assert reward4.title == "Thông Mạch Tông Sư"

    def test_canonical_default_quest_engine(self):
        from server.world.quest_engine import create_default_quest_engine

        engine = create_default_quest_engine()
        player_id = "canonical_player_01"
        engine.initialize_player(player_id, player_level=60)

        # Check default registered quests count
        assert len(engine._quest_registry) >= 13
        assert len(engine._milestone_registry) == 5

        # Normal quest 1 available
        avail = engine.get_available_quests(player_id, player_level=60)
        assert any(q.quest_id == "quest_norm_01_thuc_tinh_tang_kiem" for q in avail)

        # Progress quest 1
        assert engine.accept_quest(player_id, "quest_norm_01_thuc_tinh_tang_kiem", 60)
        engine.record_kill(player_id, "mob_trash_bone_hound", 5)
        ok, rew, _ = engine.claim_quest_reward(player_id, "quest_norm_01_thuc_tinh_tang_kiem")
        assert ok is True
        assert rew.title == "Hoang Giới Kẻ Tỉnh Giấc"

        # Check hidden quest trigger for 5 elements
        triggered = engine.trigger_hidden_quest(
            player_id,
            HiddenTriggerType.FIVE_ELEMENTS_STREAK,
            {"element_streak": True},
        )
        assert triggered == "quest_hidden_03_ngu_hanh_tuong_sinh"

        # Complete hidden quest
        engine.record_discovery(player_id, "loc_five_elements_altar")
        ok_h, rew_h, _ = engine.claim_quest_reward(player_id, "quest_hidden_03_ngu_hanh_tuong_sinh")
        assert ok_h is True
        assert rew_h.title == "Ngũ Hành Hóa Cảnh"

