"""
전략 공통 유통주식수 메타 — 라이브(WSManager) · 백테(stock_share_meta 테이블) 공용.

키움 ka10001 만 ``dstr_stk``(유통주식수)를 제공한다. 일괄 API(ka10095/ka10099)는 상장주식수만
있어 회전율 분모로는 부적합 → 후보·보유 종목만 종목별 ka10001 호출 + DB 캐시.
"""
from __future__ import annotations

import datetime
from typing import Any, Dict, Iterable, List, Optional

from kis_trader.utils.env import get_env_from_db
from kis_trader.utils.logger import get_logger

logger = get_logger("kis_trader.share")


def share_denom_key() -> str:
    """회전율 분모 필드 — ``STOCK_SHARE_DENOM`` 우선, 레거시 ``BREAKOUT_SHARE_DENOM`` 폴백."""
    raw = (
        get_env_from_db("STOCK_SHARE_DENOM", "")
        or get_env_from_db("BREAKOUT_SHARE_DENOM", "dstr_stk")
        or "dstr_stk"
    )
    return str(raw).strip().lower()


def share_denom_from_meta(meta: Optional[Dict[str, int]]) -> float:
    """
    캐시/DB 행에서 회전율 분모(float) 반환.
    유통(`dstr_stk`) 우선 설정이어도 값이 없으면 상장(`flo_stk`) 폴백.
    """
    if not meta:
        return 0.0
    key = share_denom_key()
    flo = float(meta.get("flo_stk") or 0)
    dstr = float(meta.get("dstr_stk") or 0)
    if key in ("flo_stk", "flo", "listed", "상장"):
        return flo if flo > 0 else dstr
    return dstr if dstr > 0 else flo


def upsert_stock_share_meta_row(
    db,
    code: str,
    flo_stk: int,
    dstr_stk: int,
    *,
    dstr_rt: Optional[float] = None,
    source: str = "ka10001",
) -> None:
    """stock_share_meta 테이블에 즉시 저장 (atomic)."""
    code = str(code or "").strip()
    if not code or not db:
        return
    now_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    try:
        with db.conn:
            db.conn.execute(
                """
                INSERT INTO stock_share_meta
                    (code, flo_stk, dstr_stk, dstr_rt, source, updated_at)
                VALUES (?, ?, ?, ?, ?, ?)
                ON DUPLICATE KEY UPDATE
                    flo_stk = VALUES(flo_stk),
                    dstr_stk = VALUES(dstr_stk),
                    dstr_rt = COALESCE(VALUES(dstr_rt), dstr_rt),
                    source = VALUES(source),
                    updated_at = VALUES(updated_at)
                """,
                (
                    code,
                    int(flo_stk or 0),
                    int(dstr_stk or 0),
                    float(dstr_rt) if dstr_rt is not None else None,
                    str(source or "ka10001"),
                    now_str,
                ),
            )
    except Exception as e:
        logger.warning("stock_share_meta 저장 실패 (%s): %s", code, e)


def load_stock_share_meta_map(
    db,
    codes: Optional[Iterable[str]] = None,
) -> Dict[str, Dict[str, int]]:
    """DB → {code: {flo_stk, dstr_stk}}."""
    out: Dict[str, Dict[str, int]] = {}
    if not db:
        return out
    try:
        if codes is not None:
            code_list = sorted({str(c).strip() for c in codes if c})
            if not code_list:
                return out
            placeholders = ",".join("?" * len(code_list))
            rows = db.conn.execute(
                f"""
                SELECT code, flo_stk, dstr_stk
                FROM stock_share_meta
                WHERE code IN ({placeholders})
                """,
                code_list,
            ).fetchall()
        else:
            rows = db.conn.execute(
                "SELECT code, flo_stk, dstr_stk FROM stock_share_meta",
            ).fetchall()
        for r in rows:
            code = str(r["code"]).strip()
            out[code] = {
                "flo_stk": int(r["flo_stk"] or 0),
                "dstr_stk": int(r["dstr_stk"] or 0),
            }
    except Exception as e:
        logger.debug("stock_share_meta 로드 실패: %s", e)
    return out


def load_share_denom_map(
    db,
    codes: Iterable[str],
) -> Dict[str, float]:
    """백테스트용 — 종목별 회전율 분모."""
    meta = load_stock_share_meta_map(db, codes)
    return {c: share_denom_from_meta(meta.get(c)) for c in codes}


def merge_share_meta_into_cache(
    cache: Dict[str, Dict[str, int]],
    db_map: Dict[str, Dict[str, int]],
) -> None:
    """메모리 캐시에 DB 맵 병합."""
    for code, info in db_map.items():
        if code not in cache:
            cache[code] = dict(info)
            continue
        for k in ("flo_stk", "dstr_stk"):
            v = int(info.get(k) or 0)
            if v > 0:
                cache[code][k] = v


def codes_missing_dstr(
    cache: Dict[str, Dict[str, int]],
    codes: Iterable[str],
) -> List[str]:
    """유통주식수가 아직 없는 종목 (ka10001 대상)."""
    missing: List[str] = []
    for raw in codes:
        code = str(raw or "").strip()
        if not code:
            continue
        info = cache.get(code)
        if not info or int(info.get("dstr_stk") or 0) <= 0:
            missing.append(code)
    return missing


def apply_fetched_meta(
    cache: Dict[str, Dict[str, int]],
    db,
    code: str,
    meta: Dict[str, int],
    *,
    dstr_rt: Optional[float] = None,
) -> None:
    """ka10001 응답 → RAM + DB."""
    code = str(code or "").strip()
    if not code or not meta:
        return
    flo = int(meta.get("flo_stk") or 0)
    dstr = int(meta.get("dstr_stk") or 0)
    if flo <= 0 and dstr <= 0:
        return
    cache[code] = {"flo_stk": flo, "dstr_stk": dstr}
    upsert_stock_share_meta_row(db, code, flo, dstr, dstr_rt=dstr_rt, source="ka10001")


def share_denom_for_code(params: Dict[str, Any], code: str) -> float:
    """백테/라이브 공통 — params 또는 share_denom_by_code 에서 분모 조회."""
    by_code = params.get("share_denom_by_code") or {}
    if code in by_code:
        v = float(by_code.get(code) or 0)
        if v > 0:
            return v
    return float(params.get("share_denom", 0) or 0)


def attach_share_denoms_to_params(
    params: Dict[str, Any],
    db,
    codes: Iterable[str],
) -> Dict[str, Any]:
    """백테 엔진 params에 ``share_denom_by_code`` 주입."""
    out = dict(params)
    code_list = list(codes)
    if not code_list:
        out.setdefault("share_denom_by_code", {})
        return out
    out["share_denom_by_code"] = load_share_denom_map(db, code_list)
    missing = [c for c, v in out["share_denom_by_code"].items() if v <= 0]
    if missing:
        logger.info(
            "ℹ️ stock_share_meta 미적재 %d종목 — 회전율 필터는 해당 종목 스킵 "
            "(fill_stock_share_meta.py 로 보강 가능)",
            len(missing),
        )
    return out
