#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
FreeExile: Autonomous Repair and Finalization Engine for 9-Language Novels.
Applies canonical titles, 113 section headings, fixes POV drifts and removes residual tokens.
Complies with docs/standards/GLOBAL_LOCALIZATION_DICTIONARY.md and AGENTS.md caps.
"""

import os
import sys
import re
import glob
from pathlib import Path

# Ensure root is in sys.path
sys.path.insert(0, os.path.abspath("."))

from tools.localization.novel_titles_catalog import CHAPTER_TITLES
from tools.localization.novel_headings_catalog import CANONICAL_SECTION_HEADINGS
from tools.localization.novel_repairs_catalog import (
    BASE_REPAIR_MAPS,
    KINH_KY_REPAIR_MAPS,
    POV_SLIP_REPLACEMENTS,
)

sys.stdout.reconfigure(encoding="utf-8")

CHAPTER_PREFIX = {
    "en": "CHAPTER",
    "zh": "第",
    "ja": "第",
    "ko": "제",
    "th": "บทที่",
    "de": "KAPITEL",
    "ru": "ГЛАВА",
    "es": "CAPÍTULO",
}

POV_MAPS = {
    "en": [
        (r"\bmy (throat|lungs|chest|body|eyes|ears|arms|legs|mouth|heart|bones|bone|breath|blood|spine|back|hands|hand|feet|vision|head|face|skin|stomach|soul|mind|marrow|dantian|ribs|muscles|fingers|palm|forehead)\b", r"his \1"),
    ],
    "es": [
        (r"\bmi (garganta|pecho|cuerpo|boca|corazón|hueso|respiración|sangre|columna|espalda|mano|cabeza|rostro|piel|estómago|alma|mente|médula|dantian|palma)\b", r"su \1"),
        (r"\bmis (pulmones|ojos|oídos|brazos|piernas|huesos|manos|costillas|músculos|dedos)\b", r"sus \1"),
    ],
    "de": [
        (r"\bmeine (Kehle|Lungen|Brust|Augen|Ohren|Arme|Beine|Knochen|Wirbelsäule|Hände|Haut|Seele|Rippen|Muskeln|Finger)\b", r"seine \1"),
        (r"\bmein (Körper|Mund|Herz|Knochen|Atem|Blut|Rücken|Kopf|Gesicht|Magen|Geist|Mark)\b", r"sein \1"),
    ],
    "zh": [
        (r"我的(喉咙|肺|胸膛|身体|眼睛|耳朵|手臂|双腿|嘴|心|骨头|呼吸|鲜血|后背|双手|手|头|脸|皮肤|胃|灵魂|丹田|指尖|手掌|骨髓)", r"他的\1"),
    ],
}

MISSING_CH21_SECT2 = {
    "de": "\n\n---\n\n### II. HERRSCHER DER TOTENLANDE: DÄMONENFÜRST TAOTIE\n\nDas ist der **Herrscher der Totenlande: Dämonenfürst Taotie** — der uralte Dämonenfürst Taotie!\n\nSein Körper ist so gewaltig, dass er den gesamten Himmel aus giftigem Eis verdunkelt, mehr als dreißig Meter hoch. Sein ganzer Körper ist mit einer Schicht zerklüfteter schwarzer Drachenschuppen bedeckt, besetzt mit scharfen Teufelshörnern, die grünes Höllenfeuer verströmen.\n\n",
    "ko": "\n\n---\n\n### II. 황역의 군주 귀왕 도철\n\n그것이 바로 **황역의 군주 귀왕 도철** — 태고의 귀왕 도철이다!\n\n그의 거대한 체구는 한독의 하늘을 완전히 가릴 정도였으며, 높이가 십 장(丈)을 넘었다. 온몸은 거칠고 검은 용비늘로 뒤덮여 있었고, 날카로운 악마의 뿔들에서는 녹색 지옥불이 뿜어져 나왔다.\n\n",
    "th": "\n\n---\n\n### II. จ้าวแดนเถื่อน ราชันมารเทาเถีย\n\nนั่นคือ **จ้าวแดนเถื่อน ราชันมารเทาเถีย** — ราชันมารเทาเถียโบราณ!\n\nร่างของมันมหึมาจนบดบังท้องฟ้าน้ำแข็งพิษจนมิด สูงกว่าสิบวา ทั้งร่างหุ้มด้วยเกล็ดมังกรดำขรุขระ ประดับด้วยเขามารแหลมคมที่พ่นไฟนรกสีเขียวออกมา\n\n",
    "ru": "\n\n---\n\n### II. ПОВЕЛИТЕЛЬ ДИКИХ ЗЕМЕЛЬ: КНЯЗЬ ДЕМОНОВ ТАОТЕ\n\nЭто и был **Повелитель Диких Земель: Князь Демонов Таоте** — древний Князь Демонов Таоте!\n\nЕго исполинское тело заслоняло все ледяное ядовитое небо, вздымаясь более чем на тридцать метров. Все его тело покрывала зазубренная черная драконья чешуя, усыпанная острыми демоническими рогами, извергающими зеленое адское пламя.\n\n",
}

def get_canonical_header(ch_num: int, lang: str) -> str:
    title = CHAPTER_TITLES[ch_num][lang]
    if lang in ("zh", "ja"):
        return f"# 第{ch_num}章：{title}"
    if lang == "ko":
        return f"# 제{ch_num}장: {title}"
    if lang == "th":
        return f"# บทที่ {ch_num}: {title}"
    prefix = CHAPTER_PREFIX.get(lang, "CHAPTER")
    return f"# {prefix} {ch_num}: {title}"

def repair_content(content: str, ch_num: int, lang: str, vi_headings: list) -> str:
    # 1. Normalize line endings
    content = content.replace("\r\n", "\n").replace("\r", "\n")

    # 2. Patch missing sections in Ch 21
    if ch_num == 21:
        if lang in MISSING_CH21_SECT2 and "### II." not in content and "## II." not in content and "二、" not in content:
            marker = "### III." if "### III." in content else "### "
            idx = content.find(marker)
            if idx != -1:
                content = content[:idx] + MISSING_CH21_SECT2[lang] + content[idx:]
        if lang == "th" and "### IV." not in content:
            idx = content.find("ใบมีดเฉือนเส้นเลือดดำทั้งหมด")
            if idx != -1:
                content = content[:idx] + "---\n\n### IV. ดาบปลิดชีพและกระจกโบราณเงาโลหิต\n\n" + content[idx:]
        if lang == "ru" and "### V." not in content:
            idx = content.find("Поток высшей силы")
            if idx != -1:
                content = content[:idx] + "---\n\n### V. ТРОН БЕЗДОМНОГО: ВЫСШЕЕ ВОЗНЕСЕНИЕ\n\n" + content[idx:]

    # 3. Replace Level-1 Title
    lines = content.split("\n")
    if lines and lines[0].startswith("#"):
        lines[0] = get_canonical_header(ch_num, lang)
    content = "\n".join(lines)

    # 4. Replace section headings using universal match
    heading_pattern = re.compile(r"^[#\uff03]{2,3}\s*.*$", re.MULTILINE)
    matches = list(heading_pattern.finditer(content))
    if len(matches) == len(vi_headings):
        offset = 0
        for i, match in enumerate(matches):
            vi_h = vi_headings[i]
            target_h = CANONICAL_SECTION_HEADINGS.get(vi_h, {}).get(lang, match.group(0))
            start, end = match.start() + offset, match.end() + offset
            content = content[:start] + target_h + content[end:]
            offset += len(target_h) - (match.end() - match.start())

    # 5. Fix regex POV maps outside dialogue quotes
    if lang in POV_MAPS:
        paragraphs = content.split("\n\n")
        repaired_p = []
        for p in paragraphs:
            if not p.strip().startswith(("—", '"', "“", "「")):
                for pat, repl in POV_MAPS[lang]:
                    p = re.sub(pat, repl, p, flags=re.IGNORECASE)
            repaired_p.append(p)
        content = "\n\n".join(repaired_p)

    # 6. Apply literal POV slip replacements
    if lang in POV_SLIP_REPLACEMENTS:
        for target, replacement in POV_SLIP_REPLACEMENTS[lang]:
            content = content.replace(target, replacement)

    # 7. Apply base token repairs & Kinh kỳ (Imperial Capital) corrections
    if lang in BASE_REPAIR_MAPS:
        for pat, repl in BASE_REPAIR_MAPS[lang]:
            content = re.sub(pat, repl, content)

    if lang in KINH_KY_REPAIR_MAPS:
        for pat, repl in KINH_KY_REPAIR_MAPS[lang]:
            content = re.sub(pat, repl, content)

    # 8. Clean up consecutive blank lines
    content = re.sub(r"\n{3,}", "\n\n", content).strip() + "\n"
    return content

def main():
    print("=== STARTING AUTONOMOUS REPAIR AND FINALIZATION ===")
    total_repaired = 0

    for ch in range(1, 25):
        # Read VI source
        vi_paths = glob.glob(f"novels/act_*/chuong_{ch:03d}_*.md")
        if not vi_paths:
            continue
        vi_path = vi_paths[0]
        vi_raw = Path(vi_path).read_text(encoding="utf-8").replace("\r\n", "\n")
        vi_headings = [l.strip() for l in vi_raw.splitlines() if l.strip().startswith("### ")]

        # Mirror VI directly into novels/locales/vi/ with LF
        vi_target_dir = Path(f"novels/locales/vi/act_{Path(vi_path).parent.name.split('_')[1]}")
        vi_target_dir.mkdir(parents=True, exist_ok=True)
        vi_target = vi_target_dir / Path(vi_path).name
        with open(vi_target, "w", encoding="utf-8", newline="\n") as fp:
            fp.write(vi_raw.strip() + "\n")
        total_repaired += 1

        # Process non-VI languages
        for lang in ["en", "zh", "ja", "ko", "th", "de", "ru", "es"]:
            lang_files = glob.glob(f"novels/locales/{lang}/act_*/chuong_{ch:03d}_*.md")
            if not lang_files:
                continue
            target_file = Path(lang_files[0])
            content = target_file.read_text(encoding="utf-8")
            repaired = repair_content(content, ch, lang, vi_headings)
            with open(target_file, "w", encoding="utf-8", newline="\n") as fp:
                fp.write(repaired)
            total_repaired += 1

    print(f"[OK] Successfully repaired and synchronized {total_repaired} chapter files!")

if __name__ == "__main__":
    main()
