"""
Validates the build model + MBD import/export (roadmap step 2) end to end:

  1. Construct a synthetic build using REAL archetype/powerset/power/
     enhancement identifiers from the parsed Homecoming database.
  2. Encode it to the |MBD;...;| wire format (to_mbd_text) - this is what a
     future "export" feature would produce.
  3. Decode that text back (from_mbd_text) and confirm a lossless round trip.
  4. Cross-resolve every identifier in the decoded build against the
     database (resolve_build) - our substitute for "known Mids Reborn
     output" validation, since we don't have a live copy of the app to diff
     a real export against: every reference must resolve against the same
     database Mids ships, or something is wrong with our schema/decoder.
  5. Feed the decoded build straight into the calculation engine
     (slot_math + set_bonus_calc) to prove the whole import -> calc pipeline
     chains together.
  6. Exercise the bare-base64 ("UnkBase64") decode path and error handling
     for malformed/unsupported input.
"""
from db import get_default_db
from attrib_mod import AttribMods
from build_model import (
    CharacterBuild, PowerData, SlotData, EnhancementData,
    all_slotted_enhancement_uids, power_slotted_enhancement_uids, resolve_build,
)
from mbd_format import decode_mbd_text, encode_mbd_text, MbdFormatError
from slot_math import aggregate_power_slots
from set_bonus_calc import compute_set_bonuses

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

print("=== Step 1: build a synthetic build from real identifiers ===")
build = CharacterBuild(
    built_with={"App": "CoH-Enhancement-Optimizer", "Version": "0.1", "Database": "Homecoming", "DatabaseVersion": "2026.1.1242"},
    level="50",
    class_name="Class_Blaster",
    origin="Science",
    alignment="Hero",
    name="Test Toon",
    comment="Synthetic build for MBD round-trip validation",
    power_sets=["Blaster_Ranged.Fire_Blast", "Pool.Leadership"],
    last_power=2,
    power_entries=[
        PowerData(
            power_name="Blaster_Ranged.Fire_Blast.Fire_Blast",
            level=1, stat_include=True, proc_include=True,
            slot_entries=[
                SlotData(level=1, enhancement=EnhancementData(uid="Crafted_Decimation_A", relative_level="Even")),
                SlotData(level=3, enhancement=EnhancementData(uid="Crafted_Decimation_B", relative_level="Even")),
                SlotData(level=5, enhancement=EnhancementData(uid="Crafted_Decimation_C", relative_level="Even")),
                SlotData(level=7, enhancement=EnhancementData(uid="Crafted_Decimation_D", relative_level="Even")),
                SlotData(level=9, enhancement=EnhancementData(uid="Crafted_Decimation_E", relative_level="Even")),
                SlotData(level=11, enhancement=EnhancementData(uid="Crafted_Achilles_Heel_C", relative_level="Even")),
            ],
        ),
        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", relative_level="Even")),
            ],
        ),
    ],
)

print(f"  archetype={build.class_name} powersets={build.power_sets} powers={[p.power_name for p in build.power_entries]}")

print("\n=== Step 2/3: encode to MBD text, decode back, check round trip ===")
mbd_text = build.to_mbd_text()
print(f"  encoded ({len(mbd_text)} chars):")
print("  " + mbd_text.splitlines()[0])
print("  " + mbd_text.splitlines()[1][:80] + "...")

decoded_build = CharacterBuild.from_mbd_text(mbd_text)
assert decoded_build == build, "round trip through the MBD wire format must be lossless"
print("  round trip: OK (decoded build == original build)")

print("\n=== Step 4: resolve every identifier against the real database ===")
report = resolve_build(db, decoded_build)
print(f"  archetype resolved: {report['archetype']}")
print(f"  powersets: {report['powersets']}")
print(f"  powers: {report['powers']}")
print(f"  enhancements resolved: {sum(1 for _, ok in report['enhancements'] if ok)}/{len(report['enhancements'])}")
if report["errors"]:
    print("  ERRORS:", report["errors"])
assert report["ok"], f"expected every identifier to resolve, got errors: {report['errors']}"
print("  resolve_build: OK, no unresolved references")

print("\n=== Step 5: feed the imported build into the calculation engine ===")
blaster = db.archetype(decoded_build.class_name)
col = blaster["column"]

fire_blast_entry = decoded_build.power_entries[0]
uids = power_slotted_enhancement_uids(fire_blast_entry)
print(f"  Fire Blast slotted UIDs: {uids}")
agg = aggregate_power_slots(db, uids, character_level=50)
for (eid, sub), v in agg.items():
    print(f"    {v['enhance_name']:<18} raw_sum={v['raw_sum']:.4f} ed_value={v['ed_value']:.4f}")
assert (2, -1) in agg  # Damage bucket present from the 5 Decimation pieces

all_uids = all_slotted_enhancement_uids(decoded_build)
print(f"  All slotted UIDs across build: {all_uids}")
totals = compute_set_bonuses(db, attrib_mods, all_uids, col, character_level=50)
recharge_bonus = [t for t in totals if t["effect_type"] == "Enhancement" and t["et_modifies"] == "RechargeTime"]
print(f"  Set bonuses computed: {len(totals)} total, {len(recharge_bonus)} recharge-flavored")
for t in recharge_bonus:
    print(f"    +{t['unit_value']*100:.2f}% recharge from {t['sources']}")
assert len(recharge_bonus) >= 1

print("\n=== Step 6: bare base64 (no header) decode path ===")
# Strip the |MBD;...;| envelope down to just the raw base64 payload, the way
# some share-link payloads arrive with no header at all (Mids' "UnkBase64" case).
import base64
from brotli_ctypes import compress as brotli_compress
raw_json = build.to_dict()
import json
serialized = json.dumps(raw_json, separators=(",", ":")).encode("utf-8")
bare_b64 = base64.b64encode(brotli_compress(serialized)).decode("ascii")
bare_decoded = decode_mbd_text(bare_b64)
assert bare_decoded == raw_json
print("  bare base64 (no header) decode: OK")

print("\n=== Step 7: error handling ===")
try:
    decode_mbd_text("|MxDz;10;10;10;HEX;|\r\nDEADBEEF")
    assert False, "expected MbdFormatError for legacy MxDz format"
except MbdFormatError as e:
    print(f"  MxDz correctly rejected: {e}")

try:
    decode_mbd_text("not a valid build string at all !!!")
    assert False, "expected MbdFormatError for garbage input"
except MbdFormatError as e:
    print(f"  garbage input correctly rejected: {e}")

try:
    bad = mbd_text.replace(mbd_text.splitlines()[0], "|MBD;71;9999;100;BASE64;|")
    decode_mbd_text(bad)
    assert False, "expected MbdFormatError for compressed-size mismatch"
except MbdFormatError as e:
    print(f"  compressed-size mismatch correctly rejected: {e}")

print("\nAll build-model/MBD assertions passed.")
