#!/usr/bin/env python3
"""
env_config fat 컬럼 정리 — 전략별 config_* 로 이전된 컬럼 제거.

1) 백업 (JSON): merged env, env_config 전체 행, 삭제 대상 KV, config_*, env_config_ext
2) env_config 를 ENV_GLOBAL_KEYS 만 가진 슬림 테이블로 교체
3) _migrate_config_table_columns 로 누락 글로벌 컬럼 추가

실행:
  cd ~/kis_bot && python3 -m kis_trader.scripts.prune_env_config_strategy_columns
  cd ~/kis_bot && python3 -m kis_trader.scripts.prune_env_config_strategy_columns --dry-run
"""
from __future__ import annotations

import argparse
import json
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Set

_ROOT = Path(__file__).resolve().parents[2]
if str(_ROOT) not in sys.path:
    sys.path.insert(0, str(_ROOT))

from config_schema import classify_config_key  # noqa: E402
from database import ENV_CONFIG_KEYS, ENV_GLOBAL_KEYS, TradeDB  # noqa: E402


def _row_to_dict(row: Any) -> Dict[str, Any]:
    if row is None:
        return {}
    if hasattr(row, "keys"):
        return {k: row[k] for k in row.keys()}
    return dict(row)


def _json_safe(val: Any) -> Any:
    if val is None:
        return None
    if isinstance(val, (int, float, bool, str)):
        return val
    return str(val)


def backup_all(db: TradeDB, out_dir: Path) -> Dict[str, Any]:
    out_dir.mkdir(parents=True, exist_ok=True)
    meta: Dict[str, Any] = {"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

    merged = db.get_merged_env_snapshot() or {}
    (out_dir / "merged_env_snapshot.json").write_text(
        json.dumps(merged, ensure_ascii=False, indent=2), encoding="utf-8",
    )
    meta["merged_keys"] = len(merged)

    phys_cols = set(db.conn.get_columns("env_config"))
    phys_cols -= {"id", "created_at"}
    global_set = set(ENV_GLOBAL_KEYS)
    drop_cols = sorted(c for c in phys_cols if c not in global_set)
    keep_cols = [k for k in ENV_GLOBAL_KEYS if k in phys_cols]
    meta["physical_cols"] = len(phys_cols)
    meta["keep_cols"] = len(keep_cols)
    meta["drop_cols"] = len(drop_cols)
    (out_dir / "drop_column_names.json").write_text(
        json.dumps(drop_cols, ensure_ascii=False, indent=2), encoding="utf-8",
    )

    rows = db.conn.execute("SELECT * FROM env_config ORDER BY id").fetchall()
    full_rows: List[Dict[str, Any]] = []
    dropped_kv: List[Dict[str, Any]] = []
    for raw in rows or []:
        row = _row_to_dict(raw)
        rid = row.get("id")
        full_rows.append({k: _json_safe(v) for k, v in row.items()})
        kv: Dict[str, Any] = {"id": rid, "created_at": row.get("created_at")}
        for col in drop_cols:
            v = row.get(col)
            if v is not None and str(v).strip() != "":
                kv[col] = _json_safe(v)
        if len(kv) > 2:
            dropped_kv.append(kv)
    (out_dir / "env_config_full_rows.json").write_text(
        json.dumps(full_rows, ensure_ascii=False, indent=2), encoding="utf-8",
    )
    (out_dir / "env_config_dropped_columns_kv.json").write_text(
        json.dumps(dropped_kv, ensure_ascii=False, indent=2), encoding="utf-8",
    )
    meta["env_config_rows"] = len(full_rows)

    for tbl in (
        "config_scalp", "config_short", "config_momentum", "config_breakout",
        "config_range_break", "config_updow", "config_dbband", "env_config_ext",
    ):
        try:
            trows = db.conn.execute(f"SELECT * FROM {tbl}").fetchall()
            data = [_row_to_dict(r) for r in (trows or [])]
            (out_dir / f"{tbl}.json").write_text(
                json.dumps(data, ensure_ascii=False, indent=2, default=str),
                encoding="utf-8",
            )
            meta[f"{tbl}_rows"] = len(data)
        except Exception as ex:
            meta[f"{tbl}_error"] = str(ex)

    (out_dir / "backup_meta.json").write_text(
        json.dumps(meta, ensure_ascii=False, indent=2), encoding="utf-8",
    )
    return meta


def prune_env_config(db: TradeDB, dry_run: bool = False) -> None:
    phys = set(db.conn.get_columns("env_config")) - {"id", "created_at"}
    keep_in_phys = [k for k in ENV_GLOBAL_KEYS if k in phys]
    if not keep_in_phys:
        raise RuntimeError("env_config 에 유지할 글로벌 컬럼 없음")

    cols_sql = ", ".join(f"`{k}`" for k in keep_in_phys)
    gcols_all = ", ".join(f"`{k}` TEXT" for k in ENV_GLOBAL_KEYS)

    print(f"유지 컬럼 {len(keep_in_phys)} / ENV_GLOBAL 전체 {len(ENV_GLOBAL_KEYS)}")
    print(f"삭제 컬럼 {len(phys - set(ENV_GLOBAL_KEYS))} 개")

    if dry_run:
        print("[dry-run] 슬림 교체 스킵")
        return

    db.conn.execute("DROP TABLE IF EXISTS env_config_new")
    db.conn.execute(
        f"CREATE TABLE env_config_new ("
        f"id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
        f"created_at VARCHAR(30) NOT NULL, {gcols_all}"
        f") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4",
    )
    db.conn.execute(
        f"INSERT INTO env_config_new (created_at, {cols_sql}) "
        f"SELECT created_at, {cols_sql} FROM env_config",
    )
    cnt = db.conn.execute("SELECT COUNT(*) AS c FROM env_config_new").fetchone()
    n = cnt["c"] if isinstance(cnt, dict) else cnt[0]
    print(f"env_config_new 적재 {n}행")

    db.conn.execute("DROP TABLE env_config")
    db.conn.execute("RENAME TABLE env_config_new TO env_config")
    print("env_config 슬림 교체 완료")

    if hasattr(db, "_env_config_cols_cache"):
        db._env_config_cols_cache = None
    db._migrate_config_table_columns()

    try:
        from kis_trader.utils.env import invalidate_merged_env_cache
        invalidate_merged_env_cache()
    except Exception:
        pass

    after = len(db.conn.get_columns("env_config"))
    print(f"env_config 컬럼 수: {after} (id/created_at 포함)")


def main() -> int:
    ap = argparse.ArgumentParser(description="env_config 전략 컬럼 백업 후 제거")
    ap.add_argument("--dry-run", action="store_true", help="백업만, 테이블 교체 안 함")
    ap.add_argument(
        "--backup-dir",
        default="",
        help="백업 디렉터리 (기본: backups/env_config_prune_YYYYMMDD_HHMMSS)",
    )
    args = ap.parse_args()

    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    out_dir = Path(args.backup_dir) if args.backup_dir else _ROOT / "backups" / f"env_config_prune_{ts}"

    db = TradeDB()
    try:
        print(f"백업 → {out_dir}")
        meta = backup_all(db, out_dir)
        print(json.dumps(meta, ensure_ascii=False, indent=2))
        prune_env_config(db, dry_run=args.dry_run)
        if not args.dry_run:
            snap = db.get_merged_env_snapshot()
            print(f"병합 snapshot 키 수: {len(snap)} (기능 유지 확인)")
    finally:
        db.close()
    return 0


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