"""
Manual validation of set_bonus_calc.py against the real parsed Homecoming database.

Scenario:
  1. Slot 5 different Luck of the Gambler pieces (A-E) across 5 different powers,
     plus 1 more elsewhere -> should activate all 5 tiered bonuses (2/3/4/5/6pc)
     but NOT the special +7.5% global recharge (piece F is never slotted).
  2. Add piece F (Crafted_Luck_of_the_Gambler_F, the +Recharge one) -> the special
     bonus should now also activate, on top of the tiered ones.
  3. Contrive a Rule-of-Five violation: slot 6 separate LotG-recharge-bonus sources
     (achieved here by pretending we have 6 independent copies of piece F across
     6 different sets is unrealistic within one set, so instead we fabricate 6
     raw effects with the same key directly against apply_rule_of_five to check
     the capping math in isolation).
"""
import json

from db import get_default_db
from attrib_mod import AttribMods
from set_bonus_calc import compute_set_bonuses, active_bonus_effects, apply_rule_of_five

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

blaster = db.archetype("Class_Blaster")
archetype_column = blaster["column"]
print(f"Using archetype: {blaster['display_name']} (column {archetype_column})\n")

lotg_pieces = [
    "Crafted_Luck_of_the_Gambler_A",
    "Crafted_Luck_of_the_Gambler_B",
    "Crafted_Luck_of_the_Gambler_C",
    "Crafted_Luck_of_the_Gambler_D",
    "Crafted_Luck_of_the_Gambler_E",
]

print("=== Test 1: 5 tiered LotG pieces slotted (no piece F) ===")
totals = compute_set_bonuses(db, attrib_mods, lotg_pieces, archetype_column, character_level=50)
for t in totals:
    print(f"  {t['effect_type']:>10} et_modifies={t['et_modifies']:<12} value={t['unit_value']:.4f} "
          f"x{t['count_applied']} = {t['total_value']:.4f}  (slotted={t['count_slotted']}, "
          f"capped={t['capped_by_rule_of_five']}) src={t['sources'][0]}")
print(f"  total bonus effects: {len(totals)}")
recharge_bonuses = [t for t in totals if 'Recharge' in str(t.get('et_modifies')) or 'recharge' in str(t.get('effect_type')).lower()]
print(f"  (sanity) any recharge-flavored bonus present without piece F: {len(recharge_bonuses) > 0}")

print("\n=== Test 2: adding piece F (the unique +Recharge) ===")
lotg_pieces_with_f = lotg_pieces + ["Crafted_Luck_of_the_Gambler_F"]
totals2 = compute_set_bonuses(db, attrib_mods, lotg_pieces_with_f, archetype_column, character_level=50)
for t in totals2:
    print(f"  {t['effect_type']:>10} et_modifies={t['et_modifies']:<12} value={t['unit_value']:.4f} "
          f"x{t['count_applied']} = {t['total_value']:.4f} src={t['sources'][0]} bonus_label_present")
print(f"  total bonus effects: {len(totals2)} (expect {len(totals)}+1 if special bonus has effects)")

# Print raw (pre-rule-of-five) effects for test 2 so we can see the special bonus explicitly
raw2 = active_bonus_effects(db, attrib_mods, lotg_pieces_with_f, archetype_column, character_level=50)
special_effects = [e for e in raw2 if e['source_bonus'].startswith('special')]
print(f"  raw special-bonus effects found: {len(special_effects)}")
for e in special_effects:
    print(f"    -> {json.dumps(e, indent=2)}")

print("\n=== Test 3: Rule of Five capping (synthetic, 6 identical sources) ===")
fake_raw = [
    {"effect_type": "Buff", "et_modifies": "Recharge", "damage_type": "None", "mez_type": "None",
     "value": 0.05, "source_set": f"FakeSet{i}", "source_bonus": "special: fake", "source_power": "fake"}
    for i in range(6)
]
capped = apply_rule_of_five(fake_raw)
assert len(capped) == 1, f"expected 1 grouped bonus, got {len(capped)}"
c = capped[0]
print(f"  slotted={c['count_slotted']} applied={c['count_applied']} capped={c['capped_by_rule_of_five']} "
      f"total_value={c['total_value']:.4f} (expect 5 * 0.05 = 0.25)")
assert c['count_slotted'] == 6
assert c['count_applied'] == 5
assert c['capped_by_rule_of_five'] is True
assert abs(c['total_value'] - 0.25) < 1e-9

print("\n=== Test 4: fewer than 5 identical sources -> no capping ===")
fake_raw_small = fake_raw[:3]
capped_small = apply_rule_of_five(fake_raw_small)
c2 = capped_small[0]
print(f"  slotted={c2['count_slotted']} applied={c2['count_applied']} capped={c2['capped_by_rule_of_five']} "
      f"total_value={c2['total_value']:.4f} (expect 3 * 0.05 = 0.15)")
assert c2['count_applied'] == 3
assert c2['capped_by_rule_of_five'] is False

print("\n=== Test 5: the SAME unique piece slotted in multiple DIFFERENT places must stack ===")
# Regression test: I9SetData.BuildEffects (MidsReborn/Core/I9SetData.cs) loops over every
# individual slotted OCCURRENCE of a set (EnhIndexes, duplicates included), not just whether
# a given piece is present at all. An earlier version of active_bonus_effects used a `set()`
# for membership testing, which silently collapsed 3 copies of LotG's unique +recharge piece
# down to a single 7.5% instance instead of 3 separate (Rule-of-Five-eligible) instances -
# exactly the well-known "slot LotG's +recharge unique in N different toggles" strategy this
# must get right.
three_lotg_f_copies = ["Crafted_Luck_of_the_Gambler_F"] * 3
raw3 = active_bonus_effects(db, attrib_mods, three_lotg_f_copies, archetype_column, character_level=50)
recharge_effects = [e for e in raw3 if e["source_bonus"].startswith("special")]
print(f"  3 copies of the LotG +recharge unique -> {len(recharge_effects)} raw bonus instances (expect 3)")
assert len(recharge_effects) == 3

totals3 = apply_rule_of_five(raw3)
recharge_total = next(t for t in totals3 if t["et_modifies"] == "RechargeTime")
print(f"  stacked total: {recharge_total['total_value']*100:.2f}% (expect 22.50% = 3 x 7.5%, uncapped since 3<=5)")
assert abs(recharge_total["total_value"] - 0.225) < 1e-6
assert recharge_total["capped_by_rule_of_five"] is False

print("\n=== Test 6: 6 copies of the same unique piece DOES get capped by Rule of Five ===")
six_lotg_f_copies = ["Crafted_Luck_of_the_Gambler_F"] * 6
raw6 = active_bonus_effects(db, attrib_mods, six_lotg_f_copies, archetype_column, character_level=50)
totals6 = apply_rule_of_five(raw6)
recharge_total6 = next(t for t in totals6 if t["et_modifies"] == "RechargeTime")
print(f"  6 copies -> slotted={recharge_total6['count_slotted']} applied={recharge_total6['count_applied']} "
      f"total={recharge_total6['total_value']*100:.2f}% (expect capped at 5 x 7.5% = 37.50%)")
assert recharge_total6["count_slotted"] == 6
assert recharge_total6["count_applied"] == 5
assert recharge_total6["capped_by_rule_of_five"] is True
assert abs(recharge_total6["total_value"] - 0.375) < 1e-6

print("\nAll assertions passed.")
