"""GROWTH_RATE_SIGNAL_V1 — 성장률 시그널 산출기.

EPS YoY / 매출 YoY / 영업이익 YoY를 결정론적으로 합산하여 성장 라벨을 부여한다.

주 소스: GatherTradingData.json → EPS_Growth_1Y_Pct, Revenue_Growth_Pct
보완 소스: fundamental_raw_v1.json → eps_krw (현재 EPS 확인)
EPS 프록시: EPS 존재 여부 + Forward_PE 구간 (주 소스 없을 때)

라벨:
  HYPER_GROWTH  ← EPS_Growth ≥ 30% AND Revenue_Growth ≥ 20%
  GROWTH        ← EPS_Growth ≥ 10% OR Revenue_Growth ≥ 10%
  FLAT          ← -10% ≤ growth < 10%
  DECLINE       ← growth < -10%
  DATA_MISSING  ← 모든 소스 결손

buy_modifier:
  HYPER_GROWTH → +15
  GROWTH       → +8
  FLAT         →  0
  DECLINE      → -12
  DATA_MISSING → -3

단기/중기/장기 horizon 적합도:
  HYPER_GROWTH → short=HIGH, mid=HIGH, long=MEDIUM
  GROWTH       → short=MEDIUM, mid=HIGH, long=HIGH
  FLAT         → short=LOW, mid=MEDIUM, long=MEDIUM
  DECLINE      → short=LOW, mid=LOW, long=LOW
  DATA_MISSING → short=UNKNOWN, mid=UNKNOWN, long=UNKNOWN
"""
from __future__ import annotations

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

ROOT = Path(__file__).resolve().parents[1]
DEFAULT_RAW = ROOT / "Temp" / "fundamental_raw_v1.json"
DEFAULT_JSON = ROOT / "GatherTradingData.json"
DEFAULT_OUT = ROOT / "Temp" / "growth_rate_signal_v1.json"

_BUY_MODIFIER: dict[str, int] = {
    "HYPER_GROWTH": 15,
    "GROWTH": 8,
    "FLAT": 0,
    "DECLINE": -12,
    "DATA_MISSING": -3,
    "ETF_EXCLUDED": 0,
}

_HORIZON_FIT: dict[str, dict[str, str]] = {
    "HYPER_GROWTH":  {"short": "HIGH",    "mid": "HIGH",    "long": "MEDIUM"},
    "GROWTH":        {"short": "MEDIUM",  "mid": "HIGH",    "long": "HIGH"},
    "FLAT":          {"short": "LOW",     "mid": "MEDIUM",  "long": "MEDIUM"},
    "DECLINE":       {"short": "LOW",     "mid": "LOW",     "long": "LOW"},
    "DATA_MISSING":  {"short": "UNKNOWN", "mid": "UNKNOWN", "long": "UNKNOWN"},
    "ETF_EXCLUDED":  {"short": "N/A",     "mid": "N/A",     "long": "N/A"},
}


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


def _rows(v: Any) -> list[dict[str, Any]]:
    if isinstance(v, list):
        return [x for x in v if isinstance(x, dict)]
    return []


def _f(v: Any, default: float | None = None) -> float | None:
    if v is None or v == "" or v == "N/A":
        return default
    try:
        return float(v)
    except (TypeError, ValueError):
        return default


def _classify_from_growth(eps_growth: float | None, rev_growth: float | None) -> tuple[str, str]:
    """성장률 수치에서 라벨 산출."""
    if eps_growth is None and rev_growth is None:
        return "DATA_MISSING", "no_growth_data"

    # 양쪽 모두 있으면 우선 복합 판단
    if eps_growth is not None and rev_growth is not None:
        if eps_growth >= 30.0 and rev_growth >= 20.0:
            return "HYPER_GROWTH", f"eps_g={eps_growth:.1f}%_rev_g={rev_growth:.1f}%"
        if eps_growth >= 10.0 or rev_growth >= 10.0:
            return "GROWTH", f"eps_g={eps_growth:.1f}%_rev_g={rev_growth:.1f}%"
        if eps_growth >= -10.0 and rev_growth >= -10.0:
            return "FLAT", f"eps_g={eps_growth:.1f}%_rev_g={rev_growth:.1f}%"
        return "DECLINE", f"eps_g={eps_growth:.1f}%_rev_g={rev_growth:.1f}%"

    # 한쪽만 있을 때
    g = eps_growth if eps_growth is not None else rev_growth
    label_str = "eps_g" if eps_growth is not None else "rev_g"
    assert g is not None
    if g >= 30.0:
        return "HYPER_GROWTH", f"{label_str}={g:.1f}%"
    if g >= 10.0:
        return "GROWTH", f"{label_str}={g:.1f}%"
    if g >= -10.0:
        return "FLAT", f"{label_str}={g:.1f}%"
    return "DECLINE", f"{label_str}={g:.1f}%"


def _classify_proxy_pe(eps: float | None, pe: float | None) -> tuple[str, str, str]:
    """EPS + Forward_PE 기반 성장 프록시 라벨."""
    if eps is None:
        return "DATA_MISSING", "no_eps", "NONE"
    if eps <= 0:
        return "DECLINE", f"eps_neg({eps:.0f})", "LOW"
    # EPS > 0 → PE 구간으로 시장 기대 성장률 추정
    if pe is None:
        return "DATA_MISSING", "eps_positive_no_pe", "NONE"
    pe_f = float(pe)
    if pe_f <= 0:
        return "DATA_MISSING", f"pe_invalid({pe_f:.1f})", "NONE"
    # 낮은 PE → 시장이 저성장 기대 or 저평가
    if pe_f < 10:
        return "FLAT", f"pe_low({pe_f:.1f})", "VERY_LOW"
    if pe_f < 20:
        return "FLAT", f"pe_moderate_low({pe_f:.1f})", "VERY_LOW"
    if pe_f < 35:
        return "GROWTH", f"pe_moderate({pe_f:.1f})", "VERY_LOW"
    if pe_f < 60:
        return "GROWTH", f"pe_high({pe_f:.1f})", "VERY_LOW"
    # PE > 60 → 매우 높은 성장 기대 OR 과열
    return "HYPER_GROWTH", f"pe_extreme({pe_f:.1f})", "VERY_LOW"


def _process_ticker(
    ticker: str,
    name: str,
    raw_row: dict[str, Any] | None,
    df_row: dict[str, Any] | None,
    is_etf: bool,
) -> dict[str, Any]:
    if is_etf:
        return {
            "ticker": ticker,
            "name": name,
            "label": "ETF_EXCLUDED",
            "buy_modifier": 0,
            "confidence": "N/A",
            "data_source": "etf_skip",
            "proxy_basis": None,
            "missing_fields": [],
            "horizon_fit": _HORIZON_FIT["ETF_EXCLUDED"],
            "is_etf": True,
        }

    missing_fields: list[str] = []
    label = "DATA_MISSING"
    confidence = "NONE"
    data_source = "none"
    proxy_basis: str | None = None

    # ── 1순위: data_feed EPS_Growth_1Y_Pct + Revenue_Growth_Pct ─────────────
    eps_g = _f(df_row.get("EPS_Growth_1Y_Pct") if df_row else None)
    rev_g = _f(df_row.get("Revenue_Growth_Pct") if df_row else None)

    if eps_g is not None or rev_g is not None:
        label, proxy_basis = _classify_from_growth(eps_g, rev_g)
        confidence = "HIGH" if (eps_g is not None and rev_g is not None) else "MEDIUM"
        data_source = "data_feed.EPS_Growth+Revenue_Growth"
    else:
        missing_fields += ["data_feed.EPS_Growth_1Y_Pct", "data_feed.Revenue_Growth_Pct"]

        # ── 2순위: EPS 절대값 + Forward_PE 프록시 ─────────────────────────────
        eps = _f(df_row.get("EPS") if df_row else None)
        pe = _f(df_row.get("Forward_PE") if df_row else None)
        if eps is None:
            missing_fields.append("data_feed.EPS")
        if pe is None:
            missing_fields.append("data_feed.Forward_PE")

        label, proxy_basis, confidence = _classify_proxy_pe(eps, pe)
        if confidence != "NONE":
            data_source = "proxy.eps_forward_pe"

    buy_modifier = _BUY_MODIFIER.get(label, -3)
    horizon_fit = _HORIZON_FIT.get(label, _HORIZON_FIT["DATA_MISSING"])

    return {
        "ticker": ticker,
        "name": name,
        "label": label,
        "buy_modifier": buy_modifier,
        "confidence": confidence,
        "data_source": data_source,
        "proxy_basis": proxy_basis,
        "missing_fields": missing_fields,
        "horizon_fit": horizon_fit,
        "is_etf": False,
    }


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

    raw_path = Path(args.raw) if Path(args.raw).is_absolute() else ROOT / args.raw
    json_path = Path(args.json) if Path(args.json).is_absolute() else ROOT / args.json
    out_path = Path(args.out) if Path(args.out).is_absolute() else ROOT / args.out

    raw_data = _load(raw_path)
    raw_map: dict[str, dict[str, Any]] = {
        str(r.get("ticker") or ""): r
        for r in _rows(raw_data.get("rows"))
    }

    gtd = _load(json_path)
    df_list = _rows((gtd.get("data") or {}).get("data_feed"))
    df_map: dict[str, dict[str, Any]] = {str(r.get("Ticker") or ""): r for r in df_list}

    tickers_seen: set[str] = set()
    rows: list[dict[str, Any]] = []
    label_counts: dict[str, int] = {}

    for df_row in df_list:
        ticker = str(df_row.get("Ticker") or "")
        if not ticker or ticker in tickers_seen:
            continue
        tickers_seen.add(ticker)
        name = str(df_row.get("Name") or "")
        # ETF 판별: EPS/Forward_PE/PBR 모두 없으면 ETF
        is_etf = (
            df_row.get("EPS") is None
            and df_row.get("Forward_PE") is None
            and df_row.get("PBR") is None
        )
        raw_row = raw_map.get(ticker)
        if raw_row is not None:
            is_etf = bool(raw_row.get("is_etf", is_etf))

        result = _process_ticker(ticker, name, raw_row, df_row, is_etf)
        rows.append(result)
        lbl = result["label"]
        label_counts[lbl] = label_counts.get(lbl, 0) + 1

    non_etf = [r for r in rows if not r["is_etf"]]
    data_missing_pct = (
        sum(1 for r in non_etf if r["label"] == "DATA_MISSING") / len(non_etf) * 100
        if non_etf else 0.0
    )
    gate = "PASS" if non_etf else "FAIL"

    out = {
        "formula_id": "GROWTH_RATE_SIGNAL_V1",
        "gate": gate,
        "data_missing_pct": round(data_missing_pct, 1),
        "label_counts": label_counts,
        "row_count": len(rows),
        "non_etf_count": len(non_etf),
        "rows": rows,
    }
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding="utf-8")
    status = "GROWTH_RATE_SIGNAL_V1_OK" if gate != "FAIL" else "GROWTH_RATE_SIGNAL_V1_FAIL"
    print(
        f"GROWTH_RATE_SIGNAL_V1 gate={gate} rows={len(rows)} "
        f"non_etf={len(non_etf)} data_missing_pct={data_missing_pct:.1f}% labels={label_counts}"
    )
    print(status)
    return 0


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