"""
Consolidates a CharacterBuild into aggregate stats an optimizer goal can score
against: total Defense/Resistance/MezResist by (damage_type/mez_type),
Regeneration/Recovery/global Recharge bonuses, total slots used, and (for the
DPS goal) a simplified attack-chain DPS estimate.

Two contributions get combined for each buffed stat:
  1. Each selected power's OWN self-buff effects (a defense toggle's own
     +Defense, an armor's own +Resistance, etc.), scaled by whatever
     Enhancement-schedule bucket boosts that effect type - mirrors how a
     power's displayed/buffed value works in Mids (base magnitude via
     AttribMod's GetModifier, times (1 + the matching enhanced %) from
     slot_math.aggregate_power_slots).
  2. Set bonuses from every slotted set-IO across the whole build (Rule of
     Five already enforced) - these are already final magnitudes, added
     directly (see set_bonus_calc.py / attrib_mod.py's module docstring).

The effect_type-to-enhance-bucket mapping is NOT hand-special-cased per stat:
a power's own effect_type string (e.g. "Defense", "Resistance", "Damage")
matches directly against enums.E_ENHANCE's *names* for the schedule bucket
that boosts it (E_ENHANCE 3="Defense", 18="Resistance", 2="Damage", ...) -
this is the same mapping Mids itself uses (an enhancement's own `enhance_id`
effect entries are literally these same eEnhance values).
"""
from collections import defaultdict

from enums import E_ENHANCE
from attrib_mod import effect_final_value
from slot_math import aggregate_power_slots
from set_bonus_calc import compute_set_bonuses
from build_model import all_slotted_enhancement_uids, power_slotted_enhancement_uids

ENHANCE_ID_BY_NAME = {name: enh_id for enh_id, name in E_ENHANCE.items()}

# Stats we aggregate from powers' own self-buff effects. Only these effect
# types are pulled from each power's effect list; everything else (damage to
# a target, debuffs on a target, etc.) isn't part of "how tough/buffed am I".
SELF_BUFF_EFFECT_TYPES = {
    "Defense", "Resistance", "Regeneration", "Recovery", "Heal",
    "ToHit", "HitPoints", "Endurance", "MezResist",
}


def _own_power_self_buffs(db, attrib_mods, power, power_enhancement_uids, archetype_column, character_level):
    """Returns a list of {effect_type, damage_type, mez_type, value} for a
    single power's own self-buff effects, ED-adjusted-enhancement-scaled."""
    agg = aggregate_power_slots(db, power_enhancement_uids, character_level=character_level)
    out = []
    seen_keys = set()
    for eff in power.get("effects", []):
        if eff.get("to_who") != "Self":
            continue
        if eff.get("pv_mode") not in ("Any", "PvE"):
            continue
        if eff.get("effect_type") not in SELF_BUFF_EFFECT_TYPES:
            continue
        key = (eff["effect_type"], eff.get("et_modifies"), eff.get("damage_type"), eff.get("mez_type"))
        if key in seen_keys:
            continue
        seen_keys.add(key)

        base_value = effect_final_value(attrib_mods, eff, archetype_column, character_level)

        enh_id = ENHANCE_ID_BY_NAME.get(eff["effect_type"])
        enh_mult = 0.0
        if enh_id is not None:
            bucket = agg.get((enh_id, -1))
            if bucket:
                enh_mult = bucket["ed_value"]

        out.append({
            "effect_type": eff["effect_type"],
            "damage_type": eff.get("damage_type", "None"),
            "mez_type": eff.get("mez_type", "None"),
            "value": base_value * (1.0 + enh_mult),
            "source_power": power["full_name"],
        })
    return out


def compute_build_stats(db, attrib_mods, build, character_level=50):
    """Returns a dict:
      defense[damage_type] -> fraction (e.g. 0.45 = 45%)
      resistance[damage_type] -> fraction
      mez_resist[mez_type] -> fraction
      regeneration / recovery / to_hit -> fraction bonus over base (1.0 = unbuffed)
      global_recharge_bonus -> fraction (0.7 = +70% recharge)
      total_slots_used -> int
      per_power -> {power_full_name: {"raw_sum"/"ed_value" per enhance bucket}}
    """
    archetype = db.archetype(build.class_name)
    archetype_column = archetype["column"] if archetype else 0

    defense = defaultdict(float)
    resistance = defaultdict(float)
    mez_resist = defaultdict(float)
    other_self_buffs = defaultdict(float)  # (effect_type,) -> value, for Regeneration/Recovery/ToHit/HitPoints/Endurance
    per_power = {}
    total_slots_used = 0

    for pe in build.power_entries:
        if not pe.power_name:
            continue
        power = db.power(pe.power_name)
        if power is None:
            continue

        uids = power_slotted_enhancement_uids(pe)
        total_slots_used += len([u for u in uids if u])

        per_power[pe.power_name] = aggregate_power_slots(db, uids, character_level=character_level)

        for contrib in _own_power_self_buffs(db, attrib_mods, power, uids, archetype_column, character_level):
            if contrib["effect_type"] == "Defense":
                defense[contrib["damage_type"]] += contrib["value"]
            elif contrib["effect_type"] == "Resistance":
                resistance[contrib["damage_type"]] += contrib["value"]
            elif contrib["effect_type"] == "MezResist":
                mez_resist[contrib["mez_type"]] += contrib["value"]
            else:
                other_self_buffs[contrib["effect_type"]] += contrib["value"]

    # Set bonuses across the WHOLE build (Rule of Five applies build-wide, not per-power).
    all_uids = all_slotted_enhancement_uids(build)
    set_bonus_totals = compute_set_bonuses(db, attrib_mods, all_uids, archetype_column, character_level=character_level)

    global_recharge_bonus = 0.0
    for t in set_bonus_totals:
        if t["effect_type"] == "Defense":
            defense[t["damage_type"]] += t["total_value"]
        elif t["effect_type"] == "Resistance":
            resistance[t["damage_type"]] += t["total_value"]
        elif t["effect_type"] == "MezResist":
            mez_resist[t["mez_type"]] += t["total_value"]
        elif t["effect_type"] == "Enhancement" and t["et_modifies"] == "RechargeTime":
            global_recharge_bonus += t["total_value"]
        elif t["effect_type"] in ("Regeneration", "Recovery", "ToHit", "HitPoints", "Endurance"):
            other_self_buffs[t["effect_type"]] += t["total_value"]

    return {
        "defense": dict(defense),
        "resistance": dict(resistance),
        "mez_resist": dict(mez_resist),
        "regeneration_bonus": other_self_buffs.get("Regeneration", 0.0),
        "recovery_bonus": other_self_buffs.get("Recovery", 0.0),
        "to_hit_bonus": other_self_buffs.get("ToHit", 0.0),
        "hit_points_bonus": other_self_buffs.get("HitPoints", 0.0),
        "global_recharge_bonus": global_recharge_bonus,
        "total_slots_used": total_slots_used,
        "set_bonus_totals": set_bonus_totals,
        "per_power": per_power,
    }


if __name__ == "__main__":
    from db import get_default_db
    from build_model import CharacterBuild, PowerData, SlotData, EnhancementData

    db = get_default_db()
    attrib_mods = AttribMods = __import__("attrib_mod").AttribMods(db.data_dir + "/AttribMod.json")

    build = CharacterBuild(
        class_name="Class_Blaster", level="50", origin="Science", alignment="Hero", name="Stats Test",
        power_sets=["Pool.Leadership"],
        power_entries=[
            PowerData(
                power_name="Pool.Leadership.Defense", level=2, stat_include=True,
                slot_entries=[
                    SlotData(level=2, enhancement=EnhancementData(uid="Crafted_Luck_of_the_Gambler_F")),
                    SlotData(level=2, enhancement=EnhancementData(uid="Crafted_Luck_of_the_Gambler_E")),
                    SlotData(level=2, enhancement=EnhancementData(uid="Crafted_Defense_Buff")),
                ],
            ),
        ],
    )
    stats = compute_build_stats(db, attrib_mods, build, character_level=50)
    print("Defense:", {k: f"{v*100:.2f}%" for k, v in stats["defense"].items()})
    print("Global recharge bonus:", f"{stats['global_recharge_bonus']*100:.2f}%")
    print("Total slots used:", stats["total_slots_used"])
