"""
Unit Test Suite for FreeExile Server-Authoritative NPC, Dialogue & Map-Quest Interconnection System.
Theme: CỔ VÕ HOANG DÃ & HẮC ÁM LƯU ĐÀY (Grimdark Savage Primal Exile ARPG - 100% PoE2 Spirit).

Validates:
1. Feral NPC registration, spatial indexing, and zone querying.
2. Anti-cheat distance validation (Euclidean threshold gating).
3. Dynamic branching dialogue trees with survival and level gating.
4. Seamless integration with QuestEngine (Talk NPC, Deliver to NPC, quest acceptance, reward claiming).
5. Dynamic NPC dialogue adaptation based on player quest progression.
6. Hidden quest serendipity triggers via dialogue choices / blood offerings.
7. Seamless integration with ZoneEngine (Outcast Camp, Bone-Cliff Chasm, Secret Chambers).
"""

import pytest
import time
from typing import Dict, Any, List

from server.world.quest_engine import (
    QuestEngine,
    QuestType,
    QuestStatus,
    ObjectiveType,
    HiddenTriggerType,
    ObjectiveState,
    RewardItem,
    QuestReward,
    HiddenTrigger,
    QuestDefinition,
)
from server.world.zone_engine import ZoneEngine, ZoneType, ZoneEnvironment
from server.world.npc_engine import (
    NpcEngine,
    NpcRole,
    NpcServiceType,
    NpcDialogueChoice,
    NpcDialogueNode,
    NpcDialogueTree,
    NpcDefinition,
    create_canonical_npc_engine,
)


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

    # Normal quest involving talking to a feral NPC
    engine.register_quest(
        QuestDefinition(
            quest_id="quest_intro_doc_co",
            title="Sinh Tồn Nơi Vực Táng Kiếm",
            description="Tìm gặp Lão Độc Cốt tại Vực Táng Kiếm để đoạt lấy thanh tà đao mẻ sống sót.",
            quest_type=QuestType.NORMAL,
            required_level=1,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_talk_doc_co",
                    description="Đàm đạo với Lão Độc Cốt",
                    obj_type=ObjectiveType.TALK_NPC,
                    target_id="npc_doc_co_tan_kiem",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=300,
                spirit_stones=[RewardItem("currency_primordial_stone", "Hắc Sơ Thạch", 2)],
                meridian_points=1,
            ),
        )
    )

    # Deliver item quest (blood wine offering to Dune Savage)
    engine.register_quest(
        QuestDefinition(
            quest_id="quest_deliver_ancient_wine",
            title="Hiến Tế Huyết Tửu",
            description="Đem Hỗn Nguyên Tửu dâng cho Cuồng Nhân Bão Cát tại Vô Định Bão Cát để mở đường.",
            quest_type=QuestType.NORMAL,
            required_level=5,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_deliver_wine",
                    description="Giao Hỗn Nguyên Tửu cho Cuồng Nhân Bão Cát",
                    obj_type=ObjectiveType.DELIVER_TO_NPC,
                    target_id="npc_sa_mac_du_than",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=1000,
                spirit_stones=[RewardItem("currency_blood_soul_stone", "Huyết Hồn Thạch", 5)],
                meridian_points=2,
            ),
        )
    )

    # Hidden cryptic quest
    engine.register_quest(
        QuestDefinition(
            quest_id="hidden_ancient_sword_secret",
            title="Tàn Sát Di Tích Cổ Ma",
            description="Kỳ duyên đàm đạo với Lão Độc Cốt hé lộ hố chôn tàn binh Cổ Ma.",
            quest_type=QuestType.HIDDEN,
            required_level=1,
            prerequisite_quest_ids=[],
            objectives=[
                ObjectiveState(
                    objective_id="obj_uncover_secret",
                    description="Khám phá hố chôn tàn binh",
                    obj_type=ObjectiveType.DISCOVER_LOCATION,
                    target_id="loc_ancient_stele",
                    target_count=1,
                )
            ],
            reward=QuestReward(
                exp=2500,
                spirit_stones=[RewardItem("currency_blood_reflection_mirror", "Huyết Ảnh Cổ Kính", 1)],
                meridian_points=5,
            ),
            hidden_trigger=HiddenTrigger(
                trigger_type=HiddenTriggerType.NPC_DIALOGUE_CHOICE,
                description="Đàm luận bí mật tàn sát với Lão Độc Cốt",
            ),
        )
    )

    return engine


@pytest.fixture
def zone_engine() -> ZoneEngine:
    return ZoneEngine()


@pytest.fixture
def npc_engine() -> NpcEngine:
    return create_canonical_npc_engine()


class TestNpcSystem:
    def test_npc_registration_and_zone_querying(self, npc_engine: NpcEngine) -> None:
        """Kiểm tra đăng ký NPC hoang dã và truy vấn danh sách NPC theo bản đồ."""
        sanctuary_npcs = npc_engine.get_npcs_in_zone("zone_boundless_sanctuary")
        assert len(sanctuary_npcs) >= 4

        npc_ids = {npc.npc_id for npc in sanctuary_npcs}
        assert "npc_bach_hieu_sinh" in npc_ids
        assert "npc_van_gioi_thuong_nhan" in npc_ids
        assert "npc_thiet_tuong_au_da_tu" in npc_ids

        # Truy vấn NPC theo bán kính không gian
        nearby = npc_engine.get_nearby_npcs("zone_boundless_sanctuary", 0.0, 0.0, radius=50.0)
        assert len(nearby) > 0

    def test_anticheat_proximity_validation(self, npc_engine: NpcEngine) -> None:
        """Kiểm tra cơ chế Server-Authoritative chống gian lận tọa độ tương tác."""
        npc = npc_engine.get_npc("npc_doc_co_tan_kiem")
        assert npc is not None
        assert npc.name == "Lão Độc Cốt"

        # Tương tác cự ly gần (< 5m) -> Hợp lệ
        assert npc_engine.validate_interaction_distance(npc, npc.x + 2.0, npc.y + 1.0) is True

        # Tương tác cách xa 50m -> Bác bỏ (Anti-Teleport / Ghost Interaction)
        assert npc_engine.validate_interaction_distance(npc, npc.x + 50.0, npc.y) is False

        # Tương tác sai bản đồ
        success, msg, node, _ = npc_engine.interact_with_npc(
            player_id="player_01",
            npc_id="npc_doc_co_tan_kiem",
            zone_id="zone_boundless_sanctuary", # Sai zone! (Lão Độc Cốt ở Táng Kiếm Nhai)
            player_x=npc.x,
            player_y=npc.y,
            player_level=1,
        )
        assert success is False
        assert "bản đồ" in msg.lower() or "zone" in msg.lower()

    def test_branching_dialogue_flow(self, npc_engine: NpcEngine) -> None:
        """Kiểm tra luồng hội thoại phân nhánh sinh tồn và chuyển node."""
        npc = npc_engine.get_npc("npc_doc_co_tan_kiem")
        assert npc is not None

        # Khởi tạo đàm đạo
        success, msg, initial_node, _ = npc_engine.interact_with_npc(
            player_id="player_01",
            npc_id=npc.npc_id,
            zone_id=npc.zone_id,
            player_x=npc.x,
            player_y=npc.y,
            player_level=1,
        )
        assert success is True
        assert initial_node is not None
        assert len(initial_node.choices) > 0

        # Chọn phương án hỏi thăm
        first_choice = initial_node.choices[0]
        ok, choice_msg, next_node, service, payload = npc_engine.select_dialogue_choice(
            player_id="player_01",
            npc_id=npc.npc_id,
            current_node_id=initial_node.node_id,
            choice_id=first_choice.choice_id,
        )
        assert ok is True
        assert next_node is not None
        assert next_node.node_id == first_choice.target_node_id

    def test_npc_quest_interaction_talk_objective(
        self, npc_engine: NpcEngine, quest_engine: QuestEngine
    ) -> None:
        """Kiểm tra NPC cập nhật mục tiêu TALK_NPC trong QuestEngine."""
        player_id = "player_swordsman"
        quest_engine.initialize_player(player_id, player_level=1)
        
        # Nhận nhiệm vụ
        accepted = quest_engine.accept_quest(player_id, "quest_intro_doc_co", player_level=1)
        assert accepted is True

        prog_before = quest_engine.get_player_quest(player_id, "quest_intro_doc_co")
        assert prog_before is not None
        assert prog_before.objectives["obj_talk_doc_co"].is_completed is False

        # Người chơi đến gặp Lão Độc Cốt
        npc = npc_engine.get_npc("npc_doc_co_tan_kiem")
        assert npc is not None
        
        success, _, _, _ = npc_engine.interact_with_npc(
            player_id=player_id,
            npc_id=npc.npc_id,
            zone_id=npc.zone_id,
            player_x=npc.x,
            player_y=npc.y,
            player_level=1,
            quest_engine=quest_engine,
        )
        assert success is True

        # Kiểm tra QuestEngine đã ghi nhận hoàn thành mục tiêu TALK_NPC
        prog_after = quest_engine.get_player_quest(player_id, "quest_intro_doc_co")
        assert prog_after is not None
        assert prog_after.objectives["obj_talk_doc_co"].is_completed is True
        assert prog_after.status == QuestStatus.COMPLETED

    def test_npc_quest_deliver_item_objective(
        self, npc_engine: NpcEngine, quest_engine: QuestEngine
    ) -> None:
        """Kiểm tra NPC tiếp nhận vật phẩm hiến tế (DELIVER_TO_NPC) và hoàn thành nhiệm vụ."""
        player_id = "player_traveller"
        quest_engine.initialize_player(player_id, player_level=5)
        quest_engine.accept_quest(player_id, "quest_deliver_ancient_wine", player_level=5)

        npc = npc_engine.get_npc("npc_sa_mac_du_than")
        assert npc is not None
        assert npc.name == "Cuồng Nhân Bão Cát"

        # Giao nộp vật phẩm cho NPC thông qua NpcEngine
        ok, msg, updated_quests = npc_engine.deliver_item_to_npc(
            player_id=player_id,
            npc_id=npc.npc_id,
            item_id="item_hon_nguyen_tuu",
            count=1,
            quest_engine=quest_engine,
        )
        assert ok is True
        assert "quest_deliver_ancient_wine" in updated_quests

        progress = quest_engine.get_player_quest(player_id, "quest_deliver_ancient_wine")
        assert progress is not None
        assert progress.status == QuestStatus.COMPLETED

    def test_npc_adaptive_dialogue_based_on_quest_state(
        self, npc_engine: NpcEngine, quest_engine: QuestEngine
    ) -> None:
        """Kiểm tra hội thoại của NPC tự động biến chuyển theo trạng thái tiến độ của nhiệm vụ."""
        player_id = "player_adaptive"
        npc = npc_engine.get_npc("npc_doc_co_tan_kiem")
        assert npc is not None

        # 1. Trạng thái Chưa nhận nhiệm vụ -> Hội thoại chào hỏi mở đầu
        _, _, node_initial, _ = npc_engine.interact_with_npc(
            player_id=player_id,
            npc_id=npc.npc_id,
            zone_id=npc.zone_id,
            player_x=npc.x,
            player_y=npc.y,
            player_level=1,
            quest_engine=quest_engine,
        )
        assert node_initial is not None
        assert "node_doc_co_intro" in node_initial.node_id

        # 2. Nhận nhiệm vụ -> Khi hoàn thành mục tiêu TALK, chuyển trạng thái COMPLETED
        quest_engine.accept_quest(player_id, "quest_intro_doc_co", player_level=1)
        _, _, node_inprogress, _ = npc_engine.interact_with_npc(
            player_id=player_id,
            npc_id=npc.npc_id,
            zone_id=npc.zone_id,
            player_x=npc.x,
            player_y=npc.y,
            player_level=1,
            quest_engine=quest_engine,
        )
        assert node_inprogress is not None
        assert "completed" in node_inprogress.node_id or "intro" in node_inprogress.node_id

    def test_hidden_quest_serendipity_trigger_via_npc_choice(
        self, npc_engine: NpcEngine, quest_engine: QuestEngine
    ) -> None:
        """Kiểm tra kích hoạt kỳ ngộ ẩn (Hidden Quest) qua đối thoại tàn sát với Lão Độc Cốt."""
        player_id = "player_destiny"
        npc = npc_engine.get_npc("npc_doc_co_tan_kiem")
        assert npc is not None

        # Tương tác và chọn nhánh đàm luận tàn sát bí truyền
        ok, msg, node, service, payload = npc_engine.select_dialogue_choice(
            player_id=player_id,
            npc_id=npc.npc_id,
            current_node_id="node_doc_co_martial_talk",
            choice_id="choice_inquire_ancient_sword_secret",
            quest_engine=quest_engine,
        )
        assert ok is True
        
        # Kiểm tra kỳ ngộ ẩn đã được tự động kích hoạt
        hidden_prog = quest_engine.get_player_quest(player_id, "hidden_ancient_sword_secret")
        assert hidden_prog is not None
        assert hidden_prog.status == QuestStatus.IN_PROGRESS

    def test_map_zone_integration_and_environmental_conditions(
        self, npc_engine: NpcEngine, zone_engine: ZoneEngine
    ) -> None:
        """Kiểm tra liên kết NPC với ZoneEngine và điều kiện môi trường khí tượng bão cát."""
        # Kiểm tra bản đồ Vô Định Bão Cát có NPC Cuồng Nhân Bão Cát
        sandstorm_zone = zone_engine.get_zone_definition("zone_boundless_sandstorm")
        assert sandstorm_zone is not None
        assert sandstorm_zone.environment == ZoneEnvironment.SANDSTORM

        npc_hermit = npc_engine.get_npc("npc_sa_mac_du_than")
        assert npc_hermit is not None
        assert npc_hermit.name == "Cuồng Nhân Bão Cát"
        assert npc_hermit.zone_id == "zone_boundless_sandstorm"
        assert npc_hermit.required_environment == ZoneEnvironment.SANDSTORM.value
