"""
FreeExile Ascendancy & Flesh Metamorphosis Engine.
Authoritative calculation of character transcendence, trial progression,
point allocation (10 trials x 3 points = 30 points), and Paradigm Shifters
that alter items, skills, and base attributes.
"""

from __future__ import annotations
from typing import Dict, List, Optional, Set, Tuple, Any

from server.world.ascendancy_types import (
    AscendancyTrial,
    AscendancyClassId,
    AscendancyNodeType,
    AscendancyNode,
    CharacterAscendancyState,
    AscendancyAppliedEffects,
)
from server.world.ascendancy_catalog import (
    get_canonical_trials,
    get_ascendancy_nodes,
)
from server.world.martial_types import CharacterLoadout, ChanVoDan


class AscendancyEngine:
    """
    Lõi Tính Toán Thăng Hoa & Tiến Hóa Nhân Vật (Server-Authoritative).
    Quản lý 10 Thí Luyện Cổ Đàn, cấp phát 30 điểm Thăng Hoa và áp dụng
    các biến đổi mang tính cách mạng (Paradigm Shifts) lên Item, Skill, Chỉ số.
    """

    def __init__(self) -> None:
        self.trials: Dict[int, AscendancyTrial] = {
            t.trial_index: t for t in get_canonical_trials()
        }
        self.nodes: Dict[int, AscendancyNode] = get_ascendancy_nodes()
        self.player_states: Dict[int, CharacterAscendancyState] = {}

    def get_or_create_state(self, actor_id: int) -> CharacterAscendancyState:
        if actor_id not in self.player_states:
            self.player_states[actor_id] = CharacterAscendancyState(actor_id=actor_id)
        return self.player_states[actor_id]

    def complete_trial(
        self, actor_id: int, trial_index: int, player_level: Optional[int] = None
    ) -> Tuple[bool, str, int]:
        """Hoàn thành 1 trong 10 Thử thách Cực Hạn, nhận ngay 3 điểm thăng hoa."""
        if trial_index < 1 or trial_index > 10:
            return False, f"Thí luyện #{trial_index} không tồn tại (chỉ từ 1 đến 10).", 0

        trial = self.trials[trial_index]
        if player_level is not None and player_level < trial.required_level:
            return (
                False,
                f"Yêu cầu cấp độ {trial.required_level} để mở khóa {trial.name_vi} (Cấp hiện tại: {player_level}).",
                0,
            )

        state = self.get_or_create_state(actor_id)
        if trial_index in state.completed_trials:
            return False, f"Thí luyện #{trial_index} đã hoàn thành trước đó.", 0

        if trial_index > 1 and (trial_index - 1) not in state.completed_trials:
            return False, f"Chưa hoàn thành Thí luyện #{trial_index - 1}.", 0

        state.completed_trials.add(trial_index)
        points_awarded = trial.reward_points
        msg = (
            f"Vượt qua {trial.name_vi} thành công! "
            f"Nhận được {points_awarded} Điểm Thăng Hoa. "
            f"Tiến độ: {len(state.completed_trials)}/10."
        )
        return True, msg, points_awarded

    def choose_ascendancy_class(
        self, actor_id: int, class_id: AscendancyClassId
    ) -> Tuple[bool, str]:
        """Chọn Hệ Phái Thăng Hoa sau khi đã vượt qua ít nhất 1 Thí Luyện."""
        state = self.get_or_create_state(actor_id)
        if not state.completed_trials:
            return False, "Cần hoàn thành ít nhất Thí Luyện 1 để thức tỉnh Thăng Hoa."

        if state.chosen_class is not None and state.chosen_class != class_id:
            if state.allocated_nodes:
                return False, "Phải hoàn trả toàn bộ điểm thăng hoa trước khi đổi Hệ Phái."

        state.chosen_class = class_id
        return True, f"Thức tỉnh Hệ Phái Thăng Hoa: {class_id.value} thành công."

    def get_spent_points(self, state: CharacterAscendancyState) -> int:
        return sum(
            self.nodes[nid].points_cost
            for nid in state.allocated_nodes
            if nid in self.nodes
        )

    def allocate_node(self, actor_id: int, node_id: int) -> Tuple[bool, str]:
        """Cộng điểm vào mắt xích Thăng Hoa (Minor, Notable hoặc Keystone)."""
        state = self.get_or_create_state(actor_id)
        if state.chosen_class is None:
            return False, "Chưa chọn Hệ Phái Thăng Hoa."

        if node_id not in self.nodes:
            return False, f"Mắt xích #{node_id} không tồn tại."

        node = self.nodes[node_id]
        if node.class_id != state.chosen_class:
            return False, f"Mắt xích #{node_id} không thuộc Hệ Phái {state.chosen_class.value}."

        if node_id in state.allocated_nodes:
            return False, f"Mắt xích #{node_id} đã được kích hoạt."

        spent = self.get_spent_points(state)
        available = state.total_points_earned - spent
        if available < node.points_cost:
            return False, f"Không đủ điểm Thăng Hoa (Cần {node.points_cost}, còn {available})."

        if node.prerequisite_node_ids:
            if not any(req in state.allocated_nodes for req in node.prerequisite_node_ids):
                return False, "Chưa kích hoạt mắt xích dẫn đường tiên quyết."

        state.allocated_nodes.add(node_id)
        return True, f"Kích hoạt thành công mắt xích '{node.name_vi}' ({node.node_type.value})."

    def refund_node(self, actor_id: int, node_id: int) -> Tuple[bool, str]:
        """Hoàn trả điểm mắt xích Thăng Hoa."""
        state = self.get_or_create_state(actor_id)
        if node_id not in state.allocated_nodes:
            return False, f"Mắt xích #{node_id} chưa được kích hoạt."

        for other_id in state.allocated_nodes:
            if other_id == node_id:
                continue
            other_node = self.nodes.get(other_id)
            if other_node and node_id in other_node.prerequisite_node_ids:
                return False, f"Không thể tẩy: Mắt xích '{other_node.name_vi}' đang phụ thuộc vào node này."

        state.allocated_nodes.remove(node_id)
        return True, f"Đã hoàn trả mắt xích #{node_id}."

    def _apply_keystone_rule(self, rule: str, ctx: Dict[str, Any]) -> None:
        """Thực thi từng quy tắc Keystone làm biến đổi Item, Skill hoặc Base Stats."""
        if rule == "RULE_SINGLE_WEAPON_CHAOS":
            ctx["offhand_disabled"] = True
            ctx["two_hand_boost"] = 2.5
            ctx["skills_to_chaos"] = True
        elif rule == "RULE_BONE_PLATING_ARMOR":
            ctx["bone_armor"] = True
            ctx["armor_mult"] *= 4.0
            ctx["ele_to_phys"] = 0.50
        elif rule == "RULE_NAKED_JEWELRY_BOOST":
            ctx["jewelry_disabled"] = True
            ctx["skills_dmg_boost"] *= 1.40
        elif rule == "RULE_BLOOD_PRIMACY":
            ctx["skills_cost_life"] = True
            ctx["skills_dmg_boost"] *= 1.50
        elif rule == "RULE_CONVERT_ALL_TO_CHAOS":
            ctx["skills_to_chaos"] = True
        elif rule == "RULE_STRIKE_TO_SLAM_CONVERSION":
            ctx["strikes_to_slam"] = True
            ctx["cooldown_mult"] *= 1.40
        elif rule == "RULE_CHAOS_INOCULATION_BONE":
            ctx["forced_hp"] = 1.0
            ctx["ward_mult"] *= 5.0
            ctx["chaos_immune"] = True
            ctx["poison_immune"] = True
        elif rule == "RULE_ABSOLUTE_PHYSIQUE":
            ctx["attr_conv"]["AGILITY"] = "PHYSIQUE"
            ctx["attr_conv"]["MIND"] = "PHYSIQUE"
            ctx["hp_mult"] *= 3.0
            ctx["armor_mult"] *= 3.0
        elif rule == "RULE_MIND_OVER_MATTER_SOUL":
            ctx["mom_ratio"] = 0.60
            ctx["qi_mult"] *= 4.0
            ctx["hp_mult"] *= 0.75
        elif rule == "RULE_BERSERK_SAVAGE_BLOOD":
            ctx["forced_qi"] = 0.0
            ctx["forced_ward"] = 0.0
            ctx["dmg_mult"] *= 2.0
            ctx["atk_spd"] += 0.50
        elif rule == "RULE_GODHOOD_AVATAR_METAMORPHOSIS":
            ctx["avatar_metamorphosis_active"] = True
            ctx["dmg_mult"] *= 1.40
            ctx["max_resists_bonus"] += 0.05

    def _init_mutation_context(self) -> Dict[str, Any]:
        return {
            "hp_mult": 1.0, "qi_mult": 1.0, "ward_mult": 1.0, "armor_mult": 1.0,
            "dmg_mult": 1.0, "atk_spd": 0.0, "cst_spd": 0.0, "crit_ch": 0.0,
            "crit_ml": 0.0, "pen_bn": 0.0, "forced_hp": None, "forced_qi": None,
            "forced_ward": None, "attr_conv": {}, "offhand_disabled": False,
            "jewelry_disabled": False, "two_hand_boost": 1.0, "bone_armor": False,
            "ele_to_phys": 0.0, "skills_cost_life": False, "skills_to_chaos": False,
            "strikes_to_slam": False, "skills_dmg_boost": 1.0, "cooldown_mult": 1.0,
            "chaos_immune": False, "poison_immune": False, "mom_ratio": 0.0,
            "avatar_metamorphosis_active": False, "max_resists_bonus": 0.0,
            "active_rules": [],
        }

    def calculate_mutations(
        self,
        actor_id: int,
        loadout: CharacterLoadout,
        active_skills: Optional[List[ChanVoDan]] = None,
        equipped_items: Optional[Dict[str, Any]] = None,
    ) -> AscendancyAppliedEffects:
        """Tính toán hiệu ứng cộng dồn và thực thi các Keystone Paradigm Shifts."""
        state = self.get_or_create_state(actor_id)
        ctx = self._init_mutation_context()

        for nid in state.allocated_nodes:
            node = self.nodes.get(nid)
            if not node:
                continue

            for k, v in node.stat_modifiers.items():
                if k == "damage_multiplier":
                    ctx["dmg_mult"] *= v
                elif k == "hp_multiplier":
                    ctx["hp_mult"] *= v
                elif k == "ward_multiplier":
                    ctx["ward_mult"] *= v
                elif k == "armor_multiplier":
                    ctx["armor_mult"] *= v
                elif k == "attack_speed_bonus":
                    ctx["atk_spd"] += v
                elif k == "crit_chance_bonus":
                    ctx["crit_ch"] += v
                elif k == "crit_multiplier_bonus":
                    ctx["crit_ml"] += v
                elif k == "penetration_bonus":
                    ctx["pen_bn"] += v

            if node.node_type == AscendancyNodeType.KEYSTONE and node.paradigm_rule_id:
                ctx["active_rules"].append(node.paradigm_rule_id)
                self._apply_keystone_rule(node.paradigm_rule_id, ctx)

        return AscendancyAppliedEffects(
            hp_multiplier=round(ctx["hp_mult"], 4),
            qi_multiplier=round(ctx["qi_mult"], 4),
            ward_multiplier=round(ctx["ward_mult"], 4),
            armor_multiplier=round(ctx["armor_mult"], 4),
            damage_multiplier=round(ctx["dmg_mult"], 4),
            attack_speed_bonus=round(ctx["atk_spd"], 4),
            cast_speed_bonus=round(ctx["cst_spd"], 4),
            crit_chance_bonus=round(ctx["crit_ch"], 4),
            crit_multiplier_bonus=round(ctx["crit_ml"], 4),
            penetration_bonus=round(ctx["pen_bn"], 4),
            forced_max_hp=ctx["forced_hp"],
            forced_max_qi=ctx["forced_qi"],
            forced_max_ward=ctx["forced_ward"],
            attribute_conversions=ctx["attr_conv"],
            offhand_disabled=ctx["offhand_disabled"],
            jewelry_disabled=ctx["jewelry_disabled"],
            two_hand_stat_boost=ctx["two_hand_boost"],
            armor_converts_to_bone_plate=ctx["bone_armor"],
            elemental_taken_as_physical=ctx["ele_to_phys"],
            skills_cost_life=ctx["skills_cost_life"],
            skills_convert_to_chaos=ctx["skills_to_chaos"],
            strikes_convert_to_slam=ctx["strikes_to_slam"],
            skills_damage_boost=round(ctx["skills_dmg_boost"], 4),
            cooldown_multiplier=round(ctx["cooldown_mult"], 4),
            chaos_immune=ctx["chaos_immune"],
            poison_immune=ctx["poison_immune"],
            mind_over_matter_ratio=ctx["mom_ratio"],
            avatar_metamorphosis_active=ctx["avatar_metamorphosis_active"],
            max_resists_bonus=round(ctx["max_resists_bonus"], 4),
            active_paradigm_rules=ctx["active_rules"],
        )
