from __future__ import annotations

import json
import re
from dataclasses import dataclass
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[1]
SOURCE_ROOT = Path(r"C:\Users\dmssi\Documents\GitHub\Flexible-Survival")
ENEMY_ROOT = REPO_ROOT / "game" / "scripts" / "Content" / "Enemies"
DOCS_ROOT = REPO_ROOT / "docs"

TARGET_FILES = {
    "Lyall/Ogres.i7x",
    "Nuku Valente/Female Husky.i7x",
    "Sarokcat/Alpha Husky.i7x",
    "Song/Black Equinoid.i7x",
    "Song/Inflatable Orca.i7x",
    "Song/Koballoon.i7x",
    "Song/Rubber Drake.i7x",
    "StripeGuy/Badger.i7x",
    "StripeGuy/Opossum Sailor.i7x",
    "StripeGuy/Tenrec.i7x",
    "StripeGuy/Yuppie Mink.i7x",
    "Taelyn/Avalon Kobold.i7x",
    "Taelyn/Feral Unicorn.i7x",
}

ASSIGN_RE = re.compile(r"now\s+(.+?)\s+entry\s+is\s+(.+?);", re.IGNORECASE)
TABLE_RE = re.compile(r"table of random critters", re.IGNORECASE)


@dataclass
class EnemySource:
    relpath: str
    author: str
    stem: str
    name: str
    enemy_title: str
    enemy_name: str
    enemy_type: int
    area: str
    daycycle: int
    hp: int
    level: int
    damage: int
    strength: int
    dexterity: int
    stamina: int
    perception: int
    intelligence: int
    charisma: int
    desc: str


def _clean_value(raw: str | None) -> str:
    if raw is None:
        return ""
    value = raw.strip()
    if value.startswith('"') and value.endswith('"'):
        value = value[1:-1]
    return value.strip()


def _clean_text(raw: str | None) -> str:
    value = _clean_value(raw)
    value = value.replace("[line break]", " ")
    value = re.sub(r"\[(.*?)\]", "", value)
    value = re.sub(r"\s+", " ", value).strip()
    return value


def _safe_int(raw: str | None, default: int) -> int:
    value = _clean_value(raw)
    match = re.search(r"-?\d+", value)
    return int(match.group(0)) if match else default


def _slugify(value: str) -> str:
    slug = re.sub(r"[^a-z0-9]+", "_", value.lower()).strip("_")
    return slug or "enemy"


def _fallback_description(name: str, source_file: str) -> str:
    return (
        f"{name} is present in the original Inform source ({source_file}), but this Ren'Py "
        "port currently only has a fallback encounter shell for it."
    )


def _parse_source(path: Path) -> EnemySource | None:
    text = path.read_text(encoding="utf-8", errors="ignore")
    if not TABLE_RE.search(text):
        return None

    values: dict[str, str] = {}
    for key, value in ASSIGN_RE.findall(text):
        normalized = re.sub(r"\s+", " ", key.strip()).lower()
        values.setdefault(normalized, value.strip())

    name = _clean_value(values.get("name"))
    if not name:
        return None

    desc = _clean_text(values.get("desc"))
    if not desc or desc.lower() == "noncombaterror":
        desc = _fallback_description(name, str(path.relative_to(SOURCE_ROOT)).replace("\\", "/"))

    relpath = str(path.relative_to(SOURCE_ROOT)).replace("\\", "/")
    author, stem = relpath.split("/", 1)

    return EnemySource(
        relpath=relpath,
        author=author,
        stem=Path(stem).stem,
        name=name,
        enemy_title=_clean_value(values.get("enemy title")),
        enemy_name=_clean_value(values.get("enemy name")),
        enemy_type=_safe_int(values.get("enemy type"), 0),
        area=_clean_value(values.get("area")) or "Nowhere",
        daycycle=_safe_int(values.get("daycycle"), 0),
        hp=_safe_int(values.get("hp"), 20),
        level=_safe_int(values.get("lev"), 1),
        damage=_safe_int(values.get("wdam"), 4),
        strength=_safe_int(values.get("str"), 10),
        dexterity=_safe_int(values.get("dex"), 10),
        stamina=_safe_int(values.get("sta"), 10),
        perception=_safe_int(values.get("per"), 10),
        intelligence=_safe_int(values.get("int"), 10),
        charisma=_safe_int(values.get("cha"), 10),
        desc=desc,
    )


def _render_enemy(source: EnemySource) -> str:
    slug = _slugify(source.name)
    symbol = f"ENEMY_{slug.upper()}"
    victory_label = f"{slug}_victory"
    defeat_label = f"{slug}_defeat"
    source_note = source.relpath.replace("\\", "/")
    payload = {
        "id": f"enemy_{slug}",
        "name": source.name,
        "enemy_type": source.enemy_type,
        "infection": source.name.lower(),
        "stats": {
            "hp": source.hp,
            "level": source.level,
            "damage": source.damage,
            "strength": source.strength,
            "dexterity": source.dexterity,
            "stamina": source.stamina,
            "perception": source.perception,
            "intelligence": source.intelligence,
            "charisma": source.charisma,
        },
        "area": source.area,
        "daycycle": source.daycycle,
        "description": source.desc,
    }
    data = json.dumps(payload, indent=8, ensure_ascii=True)
    data = data.replace('"description": ', '"description": ')
    return (
        "init python:\n"
        f"    # Fallback enemy port generated from Inform source: {source_note}\n"
        f"    {symbol} = {data}\n"
        f"    {symbol}[\"attack_text\"] = [\n"
        f"        \"{source.name} presses the attack and keeps you on the defensive!\",\n"
        f"        \"{source.name} lunges at you with predatory intent!\",\n"
        "    ]\n"
        f"    {symbol}[\"special_combat\"] = \"default\"\n"
        f"    {symbol}[\"victory_scene\"] = \"{victory_label}\"\n"
        f"    {symbol}[\"defeat_scene\"] = \"{defeat_label}\"\n"
        f"    {symbol}[\"loot\"] = None\n"
        f"    {symbol}[\"loot_chance\"] = 0\n"
        f"    register_enemy({symbol})\n\n"
        f"label {victory_label}:\n"
        f"    \"{source.name} wins the encounter and leaves you reeling from the loss.\"\n"
        f"    \"The full Inform scene for this enemy has not been ported yet, but the combat encounter is now installed and reachable.\"\n"
        "    return\n\n"
        f"label {defeat_label}:\n"
        f"    \"You drive {source.name} back and seize a moment to recover.\"\n"
        f"    \"The creature withdraws before the fight can turn any further against it.\"\n"
        "    return\n"
    )


def main() -> None:
    created: list[str] = []
    audit_rows: list[dict[str, object]] = []

    for relpath in sorted(TARGET_FILES):
        source_path = SOURCE_ROOT / relpath
        parsed = _parse_source(source_path)
        if not parsed:
            audit_rows.append({"source": relpath, "status": "unparsed"})
            continue

        target_dir = ENEMY_ROOT / parsed.author
        target_dir.mkdir(parents=True, exist_ok=True)
        target_path = target_dir / f"{parsed.stem}.rpy"
        target_path.write_text(_render_enemy(parsed), encoding="utf-8")
        created.append(str(target_path.relative_to(REPO_ROOT)).replace("\\", "/"))
        audit_rows.append(
            {
                "source": relpath,
                "target": str(target_path.relative_to(REPO_ROOT)).replace("\\", "/"),
                "name": parsed.name,
                "area": parsed.area,
                "level": parsed.level,
                "status": "generated",
            }
        )

    DOCS_ROOT.mkdir(parents=True, exist_ok=True)
    (DOCS_ROOT / "missing_enemy_ports.json").write_text(
        json.dumps(audit_rows, indent=2, ensure_ascii=True) + "\n",
        encoding="utf-8",
    )

    lines = [
        "# Missing Enemy Ports",
        "",
        "Generated fallback Ren'Py enemy files for high-confidence Inform monster includes that did not already have a matching target file.",
        "",
        "| Source | Target | Name | Area | Level | Status |",
        "|---|---|---|---|---:|---|",
    ]
    for row in audit_rows:
        lines.append(
            f"| `{row.get('source', '')}` | `{row.get('target', '')}` | "
            f"`{row.get('name', '')}` | `{row.get('area', '')}` | "
            f"{row.get('level', '')} | `{row.get('status', '')}` |"
        )
    (DOCS_ROOT / "missing_enemy_ports.md").write_text("\n".join(lines) + "\n", encoding="utf-8")

    print(json.dumps({"created": created, "count": len(created)}, indent=2))


if __name__ == "__main__":
    main()
