"""기준지수(KOSPI/KOSDAQ) 일별 종가 캐시 + 벤치마크 수익률·알파.

네이버 m.stock 지수 일별시세 API에서 받아 state/sim/index_history.json 에 누적.
sim/백테스트 수익률을 '같은 기간 지수 매수후보유' 대비(알파)로 평가하기 위한 데이터원.
실패해도 raise 하지 않고 None 으로 흘려보낸다 (지표 부재 ≠ 엔진 중단).
"""
from __future__ import annotations

import json
import urllib.request

from . import config

INDEX_PATH = config.STATE_DIR / 'index_history.json'
INDICES = ('KOSPI', 'KOSDAQ')
INDEX_LABEL = {'KOSPI': '코스피', 'KOSDAQ': '코스닥'}


def norm_date(d: str) -> str:
    """'2026-06-09', '2026-06-09T...', '20260609' → 'YYYYMMDD'."""
    return (d or '').replace('-', '')[:8]


def _load() -> dict:
    try:
        return json.loads(INDEX_PATH.read_text())
    except Exception:
        return {}


def _save(data: dict) -> None:
    config.STATE_DIR.mkdir(parents=True, exist_ok=True)
    INDEX_PATH.write_text(json.dumps(data, ensure_ascii=False))


def _fetch_page(index: str, page: int, size: int) -> list[dict]:
    url = f'https://m.stock.naver.com/api/index/{index}/price?pageSize={size}&page={page}'
    req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
    with urllib.request.urlopen(req, timeout=6.0) as r:
        return json.loads(r.read().decode('utf-8', 'ignore')) or []


def backfill(pages: int = 8, size: int = 50) -> dict:
    """KOSPI/KOSDAQ 일별 종가를 pages×size 만큼 받아 캐시에 병합(idempotent). {index: 신규건수}."""
    data = _load()
    added: dict[str, int] = {}
    for idx in INDICES:
        store = data.setdefault(idx, {})
        n0 = len(store)
        for p in range(1, pages + 1):
            try:
                rows = _fetch_page(idx, p, size)
            except Exception:
                break
            if not rows:
                break
            for row in rows:
                d = norm_date(row.get('localTradedAt'))
                c = row.get('closePrice')
                if not d or not c:
                    continue
                try:
                    store[d] = float(str(c).replace(',', ''))
                except (ValueError, TypeError):
                    pass
        added[idx] = len(store) - n0
    _save(data)
    return added


def update_today() -> None:
    """최신 1페이지만 받아 오늘 종가 갱신 (스캔마다 저비용 호출, 실패 무시)."""
    try:
        backfill(pages=1, size=10)
    except Exception:
        pass


def series(index: str) -> dict:
    return _load().get(index, {})


def _nearest(store: dict, date: str, after: bool):
    """date 기준 on-or-after(after=True) / on-or-before 가장 가까운 (date, close). 없으면 None."""
    if not store:
        return None
    d = norm_date(date)
    keys = sorted(store)
    if after:
        cand = [k for k in keys if k >= d]
        k = cand[0] if cand else None
    else:
        cand = [k for k in keys if k <= d]
        k = cand[-1] if cand else None
    return (k, store[k]) if k else None


def benchmark_return(index: str, date_from: str, date_to: str):
    """[from, to] 구간 지수 등락률(%). from=on-or-after, to=on-or-before 종가. 데이터 없으면 None."""
    store = series(index)
    a = _nearest(store, date_from, after=True)
    b = _nearest(store, date_to, after=False)
    if not a or not b or a[1] <= 0 or b[0] <= a[0]:
        return None
    return round((b[1] / a[1] - 1) * 100, 2)


def compare(return_pct, date_from: str, date_to: str) -> dict:
    """기간 수익률(return_pct)을 지수 대비로 비교. {index: {'pct':지수등락, 'alpha':초과}}."""
    out: dict[str, dict] = {}
    for idx in INDICES:
        b = benchmark_return(idx, date_from, date_to)
        alpha = round(return_pct - b, 2) if (b is not None and return_pct is not None) else None
        out[idx] = {'pct': b, 'alpha': alpha}
    return out
