"""
kis_trader/backtest/breakout_tick_loader.py — ws_ticks 로드·분봉 인덱싱 (B안 백테)
"""
from __future__ import annotations

from collections import defaultdict
from typing import Any, Dict, List, Optional, Set, Tuple

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

logger = get_logger("kis_trader.breakout_tick_loader")


def _candle_keys_to_tick_range(start_key: str, end_key: str) -> Tuple[str, str]:
    """ws_candles 키(12자리) → ws_ticks tick_time(14자리) 범위."""
    s = (start_key or "")[:12]
    e = (end_key or "")[:12]
    return s + "00", e + "59"


def load_breakout_ticks_by_code(
    db,
    start_key: str,
    end_key: str,
    codes: Optional[Set[str]] = None,
    *,
    market: Optional[str] = None,
) -> Tuple[Dict[str, Dict[str, List[Dict[str, Any]]]], int]:
    """
    기간 내 ``ws_ticks`` 를 종목·분봉(YYYYMMDDHHMM) 단위로 로드.

    Returns:
        (``{code: {minute_key: [tick, ...]}}``, total_tick_rows)
    """
    mkt = (market or get_env_from_db("WS_TICK_DEFAULT_MARKET", "KR") or "KR").strip().upper()
    tt_start, tt_end = _candle_keys_to_tick_range(start_key, end_key)

    try:
        if hasattr(db, "ensure_ws_ticks_table"):
            db.ensure_ws_ticks_table()
    except Exception:
        pass

    code_filter = ""
    params: List[Any] = [mkt, tt_start, tt_end]
    if codes:
        placeholders = ",".join(["%s"] * len(codes))
        code_filter = f" AND code IN ({placeholders})"
        params.extend(sorted(codes))

    sql = f"""
        SELECT code, tick_time, price, volume, source
        FROM ws_ticks
        WHERE market = %s
          AND tick_time >= %s
          AND tick_time <= %s
          {code_filter}
        ORDER BY code, tick_time
    """
    try:
        rows = db.conn.execute(sql, tuple(params)).fetchall()
    except Exception as e:
        logger.warning("ws_ticks 조회 실패 — OHLC 폴백만 사용: %s", e)
        return {}, 0

    out: Dict[str, Dict[str, List[Dict[str, Any]]]] = defaultdict(dict)
    total = 0
    for r in rows:
        code = str(r["code"]).strip()
        tt = str(r["tick_time"])[:14]
        if len(tt) < 12:
            continue
        minute_key = tt[:12]
        tick = {
            "code": code,
            "tick_time": tt,
            "price": float(r["price"] or 0),
            "volume": int(r.get("volume") or 0),
            "source": r.get("source") or "",
        }
        bucket = out[code].setdefault(minute_key, [])
        bucket.append(tick)
        total += 1

    return dict(out), total


def tick_coverage_stats(
    codes_candles: Dict[str, List[Dict]],
    ticks_by_code: Dict[str, Dict[str, List[Dict]]],
) -> Dict[str, Any]:
    """분봉 대비 틱 보유 비율 (백테 메타용)."""
    total_bars = 0
    bars_with_ticks = 0
    codes_with_any = 0
    for code, candles in codes_candles.items():
        minute_map = ticks_by_code.get(code) or {}
        if minute_map:
            codes_with_any += 1
        for c in candles:
            ct = str(c.get("candle_time") or "")[:12]
            if not ct:
                continue
            total_bars += 1
            if minute_map.get(ct):
                bars_with_ticks += 1
    ratio = (bars_with_ticks / total_bars * 100.0) if total_bars else 0.0
    return {
        "tick_bars_total": total_bars,
        "tick_bars_covered": bars_with_ticks,
        "tick_bar_coverage_pct": round(ratio, 2),
        "tick_codes_with_data": codes_with_any,
        "tick_codes_total": len(codes_candles),
    }
