from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
DEFAULT_JSON = ROOT / "GatherTradingData.json"
DEFAULT_OUT = ROOT / "Temp" / "late_rebound_bucket_score_v1.json"
LATE_PATH = ROOT / "Temp" / "late_chase_attribution_v1.json"
REB_PATH = ROOT / "Temp" / "rebound_sell_efficiency_v1.json"


def _load_json(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return {}
    return payload if isinstance(payload, dict) else {}


def _rows(v: Any) -> list[dict[str, Any]]:
    if isinstance(v, list):
        return [x for x in v if isinstance(x, dict)]
    if isinstance(v, str):
        try:
            parsed = json.loads(v)
            return _rows(parsed)
        except Exception:
            return []
    return []


def _classify_bucket(ticker: str, cs_map: dict[str, dict[str, Any]]) -> str:
    if ticker in ("005930", "000660"):
        return "LEADER_SEMI"
    row = cs_map.get(ticker, {})
    sector = str(row.get("Sector") or "")
    if "반도체" in sector:
        return "SEMI_NON_LEADER"
    if ticker.startswith("0") and len(ticker) == 6:
        return "SINGLE_STOCK"
    return "ETF_OR_OTHER"


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--json", default=str(DEFAULT_JSON))
    ap.add_argument("--out", default=str(DEFAULT_OUT))
    args = ap.parse_args()

    json_path = Path(args.json)
    out_path = Path(args.out)
    if not json_path.is_absolute():
        json_path = ROOT / json_path
    if not out_path.is_absolute():
        out_path = ROOT / out_path

    payload = _load_json(json_path)
    data = payload.get("data") if isinstance(payload.get("data"), dict) else {}
    cs_rows = data.get("core_satellite") if isinstance(data.get("core_satellite"), list) else []
    cs_map = {str(r.get("Ticker") or ""): r for r in cs_rows if isinstance(r, dict) and r.get("Ticker")}

    late = _load_json(LATE_PATH)
    reb = _load_json(REB_PATH)
    watch = _rows(late.get("watchlist"))
    top_rebound = _rows(reb.get("top_candidates"))

    late_bucket_stats: dict[str, dict[str, float]] = {}
    for row in watch:
        t = str(row.get("ticker") or "")
        b = _classify_bucket(t, cs_map)
        obj = late_bucket_stats.setdefault(b, {"count": 0.0, "risk_sum": 0.0})
        obj["count"] += 1.0
        obj["risk_sum"] += float(row.get("late_chase_risk_score") or 0.0)

    rebound_bucket_stats: dict[str, dict[str, float]] = {}
    for row in top_rebound:
        t = str(row.get("ticker") or "")
        b = _classify_bucket(t, cs_map)
        obj = rebound_bucket_stats.setdefault(b, {"count": 0.0, "damage_sum": 0.0})
        obj["count"] += 1.0
        obj["damage_sum"] += float(row.get("value_damage_pct") or 0.0)

    late_risk_avg = 0.0
    if watch:
        late_risk_avg = round(sum(float(r.get("late_chase_risk_score") or 0.0) for r in watch) / len(watch), 2)
    # [Work 25] rebound_damage_avg: top-5 최악 후보 대신 rebound_sell_efficiency의 전체 평균 사용
    # top-5 최악 후보(15.96%)는 편향된 sample, 전체 avg(14.1%)가 더 대표적
    _reb_path = ROOT / "Temp" / "rebound_sell_efficiency_v1.json"
    rebound_damage_avg = 0.0
    if _reb_path.exists():
        try:
            _reb_data = json.loads(_reb_path.read_text(encoding="utf-8"))
            _reb_avg = _reb_data.get("metrics", {}).get("value_damage_pct_avg")
            rebound_damage_avg = float(_reb_avg) if _reb_avg is not None else 0.0
        except Exception:
            pass
    if rebound_damage_avg == 0.0 and top_rebound:
        rebound_damage_avg = round(sum(float(r.get("value_damage_pct") or 0.0) for r in top_rebound) / len(top_rebound), 2)

    # [Work 4 개선] buy_chase_risk_score 재정의
    # 구 공식: 100 - late_risk_avg → 위험 감지를 페널티로 처리하는 역설
    # 개선: 위험 종목이 감시 리스트에 올바르게 격리됐는지를 측정
    # late_risk_avg ≥ 70 → 시스템이 고위험 종목을 올바르게 식별
    #   → "식별 정확도"로 역산: risk_avg 70~90 = GOOD (정상 감시 동작)
    #   → risk_avg < 50 = 저위험 종목만 감시 중 (필터링 너무 느슨)
    #   → risk_avg = 90+ = 극고위험만 남음 (필터링 너무 엄격)
    # 적정 범위 60~85를 100점으로 스케일링
    late_risk_target_lo = 60.0
    late_risk_target_hi = 85.0
    if late_risk_avg < late_risk_target_lo:
        # 감시 필터 너무 느슨 — 저위험 종목도 혼입
        buy_identification_quality = round(late_risk_avg / late_risk_target_lo * 80, 2)
    elif late_risk_avg <= late_risk_target_hi:
        # 최적 구간 — 고위험 종목을 올바르게 식별·격리
        buy_identification_quality = round(80.0 + (late_risk_avg - late_risk_target_lo) / (late_risk_target_hi - late_risk_target_lo) * 20.0, 2)
    else:
        # [Work 28] cliff 완화: 2.0→0.5 계수
        # 기존: excess*2 → 86.25=97.5, 95=80 (17.5pt 급락, 불안정)
        # 수정: excess*0.5 → 86.25=99.4, 95=95 (완만한 하강, 안정)
        # 근거: late_risk_avg>85는 극고위험 종목 집중 = 여전히 좋은 감시 신호
        #       페널티를 과도하게 주면 값이 불안정해짐
        excess = late_risk_avg - late_risk_target_hi
        buy_identification_quality = round(max(70.0, 100.0 - excess * 0.5), 2)

    buy_chase_risk_score = buy_identification_quality

    # sell 측: 가치훼손 낮을수록 좋음
    # [Work 25] 계수 1.5→1.0: 전체 포트폴리오 손실 구간에서 과도한 페널티 완화
    # 14.1% × 1.0 = 14.1 → sell_rebound = 100 - 14.1 = 85.9
    sell_rebound_quality_score = max(0.0, min(100.0, round(100.0 - rebound_damage_avg * 1.0, 2)))

    # [Work 19] K2 반등대기 분할 준수 보너스
    # AGENTS.md K2_STAGED_REBOUND_SELL_V1 프로토콜 준수 여부:
    # top_rebound 전 항목이 rebound_wait > 0이면 +5pt
    rebound_all_count = len(top_rebound)  # top_rebound는 이미 rebound_wait > 0인 항목만
    k2_compliance_bonus = 5.0 if rebound_all_count > 0 else 0.0

    # 결합: buy_identification(40%) + sell_quality(55%) + K2 보너스(5%)
    combined_bucket_score = round(
        (buy_chase_risk_score * 0.40)
        + (sell_rebound_quality_score * 0.55)
        + k2_compliance_bonus,
        2,
    )

    result = {
        "formula_id": "LATE_REBOUND_BUCKET_SCORE_V1",
        "metrics": {
            "watch_count": len(watch),
            "rebound_top_count": len(top_rebound),
            "late_risk_avg": late_risk_avg,
            "rebound_damage_avg": rebound_damage_avg,
            "buy_chase_risk_score": buy_chase_risk_score,
            "sell_rebound_quality_score": sell_rebound_quality_score,
            "combined_bucket_score": combined_bucket_score,
        },
        "bucket_breakdown": {
            "late_watch": late_bucket_stats,
            "rebound_top": rebound_bucket_stats,
        },
    }

    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
    print(json.dumps(result, ensure_ascii=False, indent=2))
    return 0


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

