"""
Internal build representation, mirroring Mids Reborn's CharacterBuildData
schema (MidsReborn/Core/BuildFile/DataModels/*.cs) field-for-field so it can
round-trip through the MBD wire format losslessly (see mbd_format.py).

Field names keep the exact PascalCase JSON keys in from_dict/to_dict (so
decode -> re-encode is a no-op on the wire), but expose snake_case attributes
on the Python side since this is what the rest of our codebase (and any
future optimizer/UI code) will actually touch.
"""
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class SubPowerData:
    power_name: str = ""
    stat_include: bool = False

    @classmethod
    def from_dict(cls, d):
        return cls(power_name=d.get("PowerName", ""), stat_include=d.get("StatInclude", False))

    def to_dict(self):
        return {"PowerName": self.power_name, "StatInclude": self.stat_include}


@dataclass
class EnhancementData:
    uid: str = ""
    grade: str = "None"
    io_level: int = 1
    relative_level: str = "Even"
    obtained: bool = False

    @classmethod
    def from_dict(cls, d):
        if d is None:
            return None
        return cls(
            uid=d.get("Uid", ""),
            grade=d.get("Grade", "None"),
            io_level=d.get("IoLevel", 1),
            relative_level=d.get("RelativeLevel", "Even"),
            obtained=d.get("Obtained", False),
        )

    def to_dict(self):
        return {
            "Uid": self.uid,
            "Grade": self.grade,
            "IoLevel": self.io_level,
            "RelativeLevel": self.relative_level,
            "Obtained": self.obtained,
        }


@dataclass
class SlotData:
    level: int = 0
    is_inherent: bool = False
    enhancement: Optional[EnhancementData] = None
    flipped_enhancement: Optional[EnhancementData] = None

    @classmethod
    def from_dict(cls, d):
        return cls(
            level=d.get("Level", 0),
            is_inherent=d.get("IsInherent", False),
            enhancement=EnhancementData.from_dict(d.get("Enhancement")),
            flipped_enhancement=EnhancementData.from_dict(d.get("FlippedEnhancement")),
        )

    def to_dict(self):
        return {
            "Level": self.level,
            "IsInherent": self.is_inherent,
            "Enhancement": self.enhancement.to_dict() if self.enhancement else None,
            "FlippedEnhancement": self.flipped_enhancement.to_dict() if self.flipped_enhancement else None,
        }


@dataclass
class PowerData:
    power_name: str = ""
    level: int = -1
    stat_include: bool = False
    proc_include: bool = False
    variable_value: int = 0
    inherent_slots_used: int = 0
    sub_power_entries: list = field(default_factory=list)
    slot_entries: list = field(default_factory=list)

    @classmethod
    def from_dict(cls, d):
        if d is None:
            return cls()
        return cls(
            power_name=d.get("PowerName", ""),
            level=d.get("Level", -1),
            stat_include=d.get("StatInclude", False),
            proc_include=d.get("ProcInclude", False),
            variable_value=d.get("VariableValue", 0),
            inherent_slots_used=d.get("InherentSlotsUsed", 0),
            sub_power_entries=[SubPowerData.from_dict(x) for x in (d.get("SubPowerEntries") or [])],
            slot_entries=[SlotData.from_dict(x) for x in (d.get("SlotEntries") or [])],
        )

    def to_dict(self):
        return {
            "PowerName": self.power_name,
            "Level": self.level,
            "StatInclude": self.stat_include,
            "ProcInclude": self.proc_include,
            "VariableValue": self.variable_value,
            "InherentSlotsUsed": self.inherent_slots_used,
            "SubPowerEntries": [x.to_dict() for x in self.sub_power_entries],
            "SlotEntries": [x.to_dict() for x in self.slot_entries],
        }


@dataclass
class CharacterBuild:
    built_with: Optional[dict] = None   # metadata only (App/Version/Database/DatabaseVersion) - passthrough
    level: str = ""
    class_name: str = ""    # JSON key "Class" - archetype's UID-style class_name, e.g. "Class_Blaster"
    origin: str = ""
    alignment: str = ""
    name: str = ""
    comment: Optional[str] = None
    power_sets: list = field(default_factory=list)
    last_power: int = 0
    power_entries: list = field(default_factory=list)

    @classmethod
    def from_dict(cls, d):
        return cls(
            built_with=d.get("BuiltWith"),
            level=d.get("Level", ""),
            class_name=d.get("Class", ""),
            origin=d.get("Origin", ""),
            alignment=d.get("Alignment", ""),
            name=d.get("Name", ""),
            comment=d.get("Comment"),
            power_sets=list(d.get("PowerSets") or []),
            last_power=d.get("LastPower", 0),
            power_entries=[PowerData.from_dict(x) for x in (d.get("PowerEntries") or [])],
        )

    def to_dict(self):
        return {
            "BuiltWith": self.built_with,
            "Level": self.level,
            "Class": self.class_name,
            "Origin": self.origin,
            "Alignment": self.alignment,
            "Name": self.name,
            "Comment": self.comment,
            "PowerSets": list(self.power_sets),
            "LastPower": self.last_power,
            "PowerEntries": [x.to_dict() for x in self.power_entries],
        }

    @classmethod
    def from_mbd_text(cls, text):
        """Parses pasted Mids Reborn export text (any of the header/no-header
        variants mbd_format.decode_mbd_text supports) into a CharacterBuild."""
        from mbd_format import decode_mbd_text
        return cls.from_dict(decode_mbd_text(text))

    def to_mbd_text(self):
        """Serializes back into the |MBD;...;| wire format Mids Reborn can import."""
        from mbd_format import encode_mbd_text
        return encode_mbd_text(self.to_dict())


def all_slotted_enhancement_uids(build):
    """Flat list of every non-empty enhancement UID slotted anywhere in the
    build (primary + flipped/alt slots) - feed straight into
    set_bonus_calc.compute_set_bonuses."""
    uids = []
    for pe in build.power_entries:
        for slot in pe.slot_entries:
            if slot.enhancement and slot.enhancement.uid:
                uids.append(slot.enhancement.uid)
            if slot.flipped_enhancement and slot.flipped_enhancement.uid:
                uids.append(slot.flipped_enhancement.uid)
    return uids


def power_slotted_enhancement_uids(power_entry, include_flipped=False):
    """Enhancement UIDs slotted into ONE power entry - feed straight into
    slot_math.aggregate_power_slots for that power's ED-adjusted values."""
    uids = []
    for slot in power_entry.slot_entries:
        if slot.enhancement and slot.enhancement.uid:
            uids.append(slot.enhancement.uid)
        if include_flipped and slot.flipped_enhancement and slot.flipped_enhancement.uid:
            uids.append(slot.flipped_enhancement.uid)
    return uids


def resolve_build(db, build):
    """Cross-references every identifier in a decoded build (archetype,
    powersets, powers, enhancement UIDs) against the parsed Homecoming
    database. This is our stand-in for "validate against known Mids Reborn
    output": since we don't have a live copy of the app to diff against,
    every reference in an imported build must at minimum resolve against the
    exact same database Mids itself ships - if it doesn't, either the build
    string is malformed/from an incompatible database version, or our schema
    understanding is wrong somewhere. Returns a report dict; report["ok"] is
    True iff every reference resolved."""
    report = {"archetype": None, "powersets": [], "powers": [], "enhancements": [], "errors": []}

    archetype = db.archetype(build.class_name)
    report["archetype"] = archetype["display_name"] if archetype else None
    if archetype is None:
        report["errors"].append(f"Unknown archetype class_name: {build.class_name!r}")

    for ps_name in build.power_sets:
        if not ps_name:
            continue
        ps = db.powerset_by_full_name.get(ps_name)
        report["powersets"].append((ps_name, ps is not None))
        if ps is None:
            report["errors"].append(f"Unknown powerset: {ps_name!r}")

    for pe in build.power_entries:
        if not pe.power_name:
            continue
        p = db.power(pe.power_name)
        report["powers"].append((pe.power_name, p is not None))
        if p is None:
            report["errors"].append(f"Unknown power: {pe.power_name!r}")
            continue
        for slot in pe.slot_entries:
            for enh in (slot.enhancement, slot.flipped_enhancement):
                if enh is None or not enh.uid:
                    continue
                e = db.enhancement(enh.uid)
                report["enhancements"].append((enh.uid, e is not None))
                if e is None:
                    report["errors"].append(f"Unknown enhancement uid: {enh.uid!r} (in {pe.power_name})")

    report["ok"] = len(report["errors"]) == 0
    return report
