#!/usr/bin/env python3
"""
env 타임라인 — 백테스트 시 env_config / config_* 변경 시각별 스냅샷 재생.

실매·파람서치는 최신 env 1행만 사용. 웹 백테만 ``EnvTimeline`` 으로
봉 시각(candle_time)마다 그 시점까지의 DB 설정을 적용한다.
"""
from __future__ import annotations

from bisect import bisect_right
from typing import Any, Dict, List, Optional, Tuple


def _created_at_to_key(created_at: str) -> str:
    """``YYYY-MM-DD HH:MM:SS`` → ``YYYYMMDDHHMMSS``."""
    s = str(created_at or "").strip()
    if len(s) >= 19:
        return (
            s[0:4] + s[5:7] + s[8:10]
            + s[11:13] + s[14:16] + s[17:19]
        )
    return ""


def _candle_time_to_lookup_key(candle_time: str) -> str:
    """분봉 키(YYYYMMDDHHMM) → 해당 분 마감초(…59) lookup 키."""
    t = str(candle_time or "")[:12]
    if len(t) < 12:
        return ""
    return t + "59"


def _ymd_to_start_bound(ymd: str) -> str:
    y = str(ymd or "")[:8]
    if len(y) < 8:
        return ""
    return f"{y[:4]}-{y[4:6]}-{y[6:8]} 00:00:00"


class EnvTimeline:
    """env 변경 이벤트 시계열 — ``snapshot_at`` 으로 봉 시각별 merged env 조회."""

    def __init__(self, events: List[Tuple[str, Dict[str, Any]]]) -> None:
        deduped: List[Tuple[str, Dict[str, Any]]] = []
        prev_sig: Optional[Tuple[str, ...]] = None
        for key, snap in events:
            if not key:
                continue
            sig = tuple(sorted((k, str(v)) for k, v in snap.items()))
            if sig == prev_sig:
                continue
            prev_sig = sig
            deduped.append((key, dict(snap)))
        self._keys: List[str] = [e[0] for e in deduped]
        self._snaps: List[Dict[str, Any]] = [e[1] for e in deduped]

    @property
    def snapshot_count(self) -> int:
        return len(self._keys)

    def snapshot_at(self, candle_time: str) -> Tuple[Dict[str, Any], str]:
        """봉 시각 기준 적용 env 스냅샷 + 이벤트 키(YYYYMMDDHHMMSS)."""
        if not self._keys:
            return {}, ""
        qkey = _candle_time_to_lookup_key(candle_time)
        if not qkey:
            return dict(self._snaps[0]), self._keys[0]
        idx = bisect_right(self._keys, qkey) - 1
        if idx < 0:
            return {}, ""
        return dict(self._snaps[idx]), self._keys[idx]


def build_env_timeline(
    db: Any,
    start_ymd: str,
    end_ymd: str,
) -> Optional[EnvTimeline]:
    """백테 구간 env 변경 이력 → ``EnvTimeline`` (변경 없으면 1스냅샷)."""
    if db is None:
        return None
    sy = str(start_ymd or "")[:8]
    ey = str(end_ymd or "")[:8]
    if len(sy) < 8 or len(ey) < 8:
        return None
    if not hasattr(db, "list_env_change_times_in_range"):
        return None
    if not hasattr(db, "get_merged_env_snapshot_as_of"):
        return None

    start_bound = _ymd_to_start_bound(sy)
    event_times = db.list_env_change_times_in_range(sy, ey)
    if not event_times:
        event_times = [start_bound]

    events: List[Tuple[str, Dict[str, Any]]] = []
    for as_of in event_times:
        merged = db.get_merged_env_snapshot_as_of(as_of)
        key = _created_at_to_key(as_of)
        if not key:
            continue
        events.append((key, merged))

    if not events:
        return None
    return EnvTimeline(events)
