"""
AutoPOE2 - Gán ngữ nghĩa offsets.target từ báo cáo live MemProbe.

Chỉ persist RVA / offset tương đối / AOB (INV-OFFSET-01).
Địa chỉ heap trong JSON report bị bỏ qua.

  python tools/re_pipeline/label_targets.py --report bin/Release/live_label_6A9E477A.json
"""
from __future__ import annotations

import argparse
import json
import sqlite3
import sys
from datetime import datetime
from pathlib import Path

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8")

REPO_ROOT = Path(__file__).resolve().parent.parent.parent
DEFAULT_DB = REPO_ROOT / "data" / "poe2_offsets.db"
FORBIDDEN = {
    "player_addr", "xyz_addr", "shield_addr", "mana_addr",
    "spirit_addr", "ingame_addr", "terrain_addr",
    "camera_addr", "fog_density_addr",
}
ALLOWED_TARGETS = {
    "InGameState", "LocalPlayer", "EntityMap",
    "LifeComponent", "PositionComponent", "AreaInstance",
    "EnergyShield", "Ward", "Mana", "Spirit",
}


def norm_rva(value: str) -> str:
    return "0x" + format(int(str(value), 16), "X")


def apply_labels(report_path: Path, db_path: Path, toml_path: Path | None) -> int:
    payload = json.loads(report_path.read_text(encoding="utf-8"))
    if payload.get("schema") != "autopoe2.live_label/v1":
        print(f"[ERROR] Schema không hỗ trợ: {payload.get('schema')}", file=sys.stderr)
        return 1

    build_id = payload.get("build_id") or ""
    generated_at = payload.get("generated_at") or datetime.now().strftime("%d/%m/%Y %H:%M:%S")
    labels = payload.get("labels") or []
    if not labels:
        print("[WARN] Report không có labels — giữ target=unreviewed.")
        return 2

    db_path.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(str(db_path))
    updated = 0
    inserted = 0
    try:
        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS offsets (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                build_id TEXT NOT NULL,
                kind TEXT NOT NULL,
                target TEXT NOT NULL,
                payload TEXT NOT NULL,
                validator_id TEXT,
                source TEXT NOT NULL,
                discovered_at TEXT NOT NULL,
                last_verified TEXT,
                hit_count INTEGER DEFAULT 0,
                miss_count INTEGER DEFAULT 0
            )
            """
        )
        for label in labels:
            target = str(label.get("target") or "")
            kind = str(label.get("kind") or "aob_pattern")
            if target in FORBIDDEN or kind in FORBIDDEN:
                print(f"[INV-OFFSET-01] Bỏ qua khóa cấm: {target}/{kind}")
                continue
            if target not in ALLOWED_TARGETS:
                print(f"[WARN] target lạ bị bỏ: {target}")
                continue
            rva = norm_rva(str(label.get("rva") or "0"))
            evidence = str(label.get("evidence") or "")
            payload_json = json.dumps(
                {"target_rva": rva, "kind": kind, "evidence": evidence},
                ensure_ascii=False,
            )
            cur = conn.execute(
                """
                UPDATE offsets
                   SET target = ?, last_verified = ?, hit_count = hit_count + 1,
                       validator_id = 'ValidateInGameState'
                 WHERE build_id = ?
                   AND upper(json_extract(payload, '$.target_rva')) = upper(?)
                """,
                (target, generated_at, build_id, rva),
            )
            if cur.rowcount and cur.rowcount > 0:
                updated += cur.rowcount
                continue
            conn.execute(
                """
                INSERT INTO offsets (
                    build_id, kind, target, payload, validator_id, source,
                    discovered_at, last_verified, hit_count, miss_count
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, 0)
                """,
                (
                    build_id,
                    kind,
                    target,
                    payload_json,
                    "ValidateInGameState",
                    "live_label",
                    generated_at,
                    generated_at,
                ),
            )
            inserted += 1
        conn.commit()
    finally:
        conn.close()

    if toml_path and toml_path.is_file():
        annotate_toml(toml_path, labels, generated_at, payload.get("entity_map_offset"))

    print(f"[OK] labels applied: updated={updated} inserted={inserted} build={build_id}")
    print(f"     db={db_path}")
    if toml_path:
        print(f"     toml={toml_path}")
    return 0


def _unique_live_notes(labels: list, generated_at: str) -> list[str]:
    notes: list[str] = []
    seen: set[tuple[str, str]] = set()
    for label in labels:
        target = str(label.get("target") or "")
        if target in FORBIDDEN or target not in ALLOWED_TARGETS:
            continue
        rva = norm_rva(str(label.get("rva") or "0"))
        key = (target, rva)
        if key in seen:
            continue
        seen.add(key)
        notes.append(
            f"# LIVE {generated_at}: {target} rva={rva} "
            f"kind={label.get('kind')} evidence={label.get('evidence')}"
        )
    return notes


def _promote_ingame_static_root(body: list[str], labels: list) -> list[str]:
    """Đưa RVA InGameState đã đối chuẩn lên static_root_1 (hot path Core)."""
    ingame = None
    for label in labels:
        if str(label.get("target")) != "InGameState":
            continue
        ingame = norm_rva(str(label.get("rva") or "0"))
        break
    if not ingame or ingame in ("0x0", "0"):
        return body

    roots: list[tuple[int, str, str]] = []
    rest: list[str] = []
    for ln in body:
        stripped = ln.strip()
        if stripped.startswith("static_root_") and "=" in stripped:
            key, _, val = stripped.partition("=")
            try:
                idx = int(key.strip().split("_")[-1])
            except ValueError:
                rest.append(ln)
                continue
            roots.append((idx, key.strip(), val.strip()))
        else:
            rest.append(ln)
    if not roots:
        return body

    values = [norm_rva(v) for _, _, v in roots]
    if ingame not in values:
        return body
    ordered = [ingame] + [v for v in values if v != ingame]
    rebuilt = [f"static_root_{i} = {rva}" for i, rva in enumerate(ordered, start=1)]
    # Giữ các khóa khác (build stamp, AOB, …) theo thứ tự cũ, thay cụm static_root_*
    out: list[str] = []
    inserted = False
    for ln in rest:
        if not inserted and ln.strip().startswith("aob_pattern_"):
            out.extend(rebuilt)
            if out and out[-1] != "":
                out.append("")
            inserted = True
        if ln.strip().startswith("static_root_"):
            continue
        out.append(ln)
    if not inserted:
        out.extend(rebuilt)
    return out


def annotate_toml(toml_path: Path, labels: list, generated_at: str, entity_map_offset) -> None:
    text = toml_path.read_text(encoding="utf-8")
    lines = text.splitlines()
    notes = _unique_live_notes(labels, generated_at)
    header = [
        ln for ln in lines
        if ln.startswith("#") and not ln.startswith("# LIVE")
    ]
    body = [ln for ln in lines if not ln.startswith("#")]
    body = _promote_ingame_static_root(body, labels)
    if entity_map_offset and str(entity_map_offset) not in ("0x0", "0", "0x00"):
        em = norm_rva(str(entity_map_offset))
        body = [ln for ln in body if not ln.startswith("entity_map_offset")]
        body.insert(0, f"entity_map_offset = {em}")
    new_text = "\n".join(header + notes + [""] + body).rstrip() + "\n"
    toml_path.write_text(new_text, encoding="utf-8")


def main() -> int:
    parser = argparse.ArgumentParser(description="Gán offsets.target từ live_label JSON")
    parser.add_argument("--report", required=True)
    parser.add_argument("--db", default=str(DEFAULT_DB))
    parser.add_argument("--toml", default=None)
    args = parser.parse_args()
    report_path = Path(args.report)
    if not report_path.is_file():
        print(f"[ERROR] Không thấy report: {report_path}", file=sys.stderr)
        return 2
    payload = json.loads(report_path.read_text(encoding="utf-8"))
    build_id = payload.get("build_id") or ""
    toml_path = Path(args.toml) if args.toml else (
        REPO_ROOT / "bin" / "Release" / f"offsets_{build_id}.toml"
    )
    return apply_labels(report_path, Path(args.db), toml_path)


if __name__ == "__main__":
    raise SystemExit(main())
