#!/usr/bin/env python3
"""유동 목표 적용기 (Mac 디스패처용).

분석 예약작업이 시장 상황에 맞춰 산출한 종목별 목표(T1/T2/손절 등)를
outputs/proposed_targets.json 에 남기면, 이 스크립트가 live_account_state.json/.js 의
orders[] 에 '목표 레벨만' 병합한다. (수량·평단·평가액은 건드리지 않음 — 그건 매매/시세 갱신 담당)

proposed_targets.json 형식(예):
{
  "generated_at": "...", "basis": "직전고점·VWAP·ATR·지지/저항",
  "targets": {
    "NASA": {"t1":35.0,"t2":37.5,"invalid":31.0,"hold_until":37.5,"prior_high":42.87,
             "action":"HOLD_TO_REBOUND_TRIM","action_ko":"T1 25-35% 축소","target_basis":"...","note":"..."},
    ...
  }
}

규칙: 목표는 분석(전략)의 산출물이므로 정식 파일(proposed_targets.json)을 통해서만 반영.
임의 수정 금지. 적용 후 입력 파일은 targets_applied/ 로 이동.
"""
from __future__ import annotations

import datetime as dt
import json
import os

BASE = os.path.dirname(os.path.abspath(__file__))
JSON_PATH = os.path.join(BASE, "live_account_state.json")
JS_PATH = os.path.join(BASE, "live_account_state.js")
PROPOSED = os.path.join(BASE, "proposed_targets.json")
APPLIED_DIR = os.path.join(BASE, "targets_applied")
LOG_PATH = os.path.join(BASE, "targets_apply_log.jsonl")
JS_PREFIX = "window.LIVE_ACCOUNT_STATE = "

# orders 에 병합 허용하는 '목표 레벨' 필드만 (수량/평가/손익 등은 제외)
ALLOWED = {"t1", "t2", "invalid", "hold_until", "prior_high",
           "action", "action_ko", "target_basis", "note"}


def log(rec: dict) -> None:
    rec["ts"] = dt.datetime.now(dt.timezone.utc).isoformat()
    try:
        with open(LOG_PATH, "a", encoding="utf-8") as f:
            f.write(json.dumps(rec, ensure_ascii=False) + "\n")
    except Exception:
        pass


def atomic_write(path: str, text: str) -> None:
    tmp = path + ".tmp"
    with open(tmp, "w", encoding="utf-8") as f:
        f.write(text)
    os.replace(tmp, path)


def main() -> int:
    if not os.path.exists(PROPOSED) or not os.path.exists(JSON_PATH):
        return 0
    try:
        prop = json.load(open(PROPOSED, encoding="utf-8"))
    except Exception as e:
        log({"event": "bad_proposed", "err": str(e)[:200]})
        return 0
    targets = prop.get("targets") or {}
    if not targets:
        return 0

    state = json.load(open(JSON_PATH, encoding="utf-8"))
    changed = []
    for o in state.get("orders", []):
        t = targets.get(o.get("ticker"))
        if not t:
            continue
        before = {k: o.get(k) for k in ("t1", "t2", "invalid")}
        for k, v in t.items():
            if k in ALLOWED and v not in (None, ""):
                o[k] = v
        o["target_updated_at"] = prop.get("generated_at") or dt.datetime.now(dt.timezone.utc).astimezone().isoformat()
        if before != {k: o.get(k) for k in ("t1", "t2", "invalid")}:
            changed.append(o.get("ticker"))

    state["targets_basis"] = prop.get("basis", "")
    state["targets_updated_at"] = prop.get("generated_at") or dt.datetime.now(dt.timezone.utc).astimezone().isoformat()

    text = json.dumps(state, ensure_ascii=False, indent=2)
    atomic_write(JSON_PATH, text)
    atomic_write(JS_PATH, JS_PREFIX + text + ";\n")

    os.makedirs(APPLIED_DIR, exist_ok=True)
    stamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S")
    os.replace(PROPOSED, os.path.join(APPLIED_DIR, f"{stamp}_proposed_targets.json"))
    log({"event": "applied", "changed": changed, "tickers": list(targets.keys())})
    print(f"목표 반영: {len(targets)}종목 (변경 {changed or '없음'})")
    return 0


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