"""
kis_trader/utils/env.py — DB(config_* + env_config 병합) 우선 + os.environ 폴백 환경변수 헬퍼
===========================================================================
기존 kis_scalping_ver2 / kis_short_ver3 에 흩어져 있던 `get_env_*` 를 통합.
- DB get_merged_env_snapshot / get_strategy_env_dict → os.environ → default 순으로 조회.
- 하드코딩 금지 원칙에 맞춰 전 모듈에서 이 함수들만 사용하도록 한다.
"""
from __future__ import annotations

import logging
import os
from typing import Any, Dict, Optional

logger = logging.getLogger("kis_trader.env")

# 순환 import 방지용 레이지 TradeDB 참조
_db_instance = None
# get_merged_env_snapshot() — 프로세스당 1회 (백테·파라서치 env 키 반복 조회 가속)
_merged_env_cache: Optional[Dict[str, str]] = None
# env 캐시 세대 카운터 — invalidate 시 +1. 파생 캐시(예: whipsaw 파라미터)가
# 이 값으로 무효화를 감지해 안전하게 재계산한다(값은 그대로, 재계산 시점만 동일).
_env_generation: int = 0


def _get_db():
    """TradeDB 싱글톤. 최초 호출 시 MariaDB 연결."""
    global _db_instance
    if _db_instance is None:
        try:
            from database import TradeDB  # 프로젝트 루트의 기존 모듈
            _db_instance = TradeDB()
        except Exception as e:
            logger.warning("TradeDB 초기화 실패(%s) → env_config 조회 불가 → os.environ 폴백", e)
            _db_instance = False  # 실패 기록 (None과 구분)
    return _db_instance or None


def set_db(db_obj) -> None:
    """외부에서 이미 만든 TradeDB 인스턴스를 재사용할 때 주입."""
    global _db_instance
    _db_instance = db_obj


def invalidate_merged_env_cache() -> None:
    """env/config 저장 후 스냅샷 캐시 무효화 (insert_env_snapshot 등)."""
    global _merged_env_cache, _env_generation
    _merged_env_cache = None
    _env_generation += 1


def env_cache_generation() -> int:
    """현재 env 캐시 세대. invalidate 될 때마다 증가.

    파생 캐시가 이 값을 키에 포함하면, 설정 저장(무효화) 시 자동으로 재계산되고
    그 전까지는 동일 값을 재사용한다(get_env_from_db 의 병합캐시와 동일 수명).
    """
    return _env_generation


def _strip_comment(val: Any) -> Any:
    """DB 컬럼에 `#` 이후 주석이 들어 있을 때 제거. 기존 규칙 유지."""
    if isinstance(val, str) and "#" in val:
        return val.split("#", 1)[0].strip()
    return val


def get_merged_env_dict() -> dict:
    """env_config + config_scalp/short/momentum/… 최신 행 병합 flat dict."""
    global _merged_env_cache
    if _merged_env_cache is not None:
        return _merged_env_cache

    db = _get_db()
    if db is None:
        return {}
    try:
        if hasattr(db, "get_merged_env_snapshot"):
            merged = db.get_merged_env_snapshot()
        else:
            row = db.get_latest_env()
            merged = dict((row or {}).get("snapshot") or {})
        _merged_env_cache = dict(merged or {})
        return _merged_env_cache
    except Exception as e:
        logger.debug("merged env 조회 실패: %s", e)
        return {}


def get_strategy_env_dict(strategy_id: str) -> dict:
    """전략 config_* 테이블 + 공통 env 병합 (전략 키 우선).

    SCALP → config_scalp, MOMENTUM → config_momentum, SHORT → config_short …
    실매(get_env_from_db) · 웹 · 파라서치가 동일 소스를 쓰도록 한다.
    """
    merged = get_merged_env_dict()
    db = _get_db()
    if db is None:
        return merged
    try:
        if hasattr(db, "get_strategy_config_snapshot"):
            strat = db.get_strategy_config_snapshot(strategy_id)
            if strat:
                out = dict(merged)
                out.update(strat)
                return out
    except Exception as e:
        logger.debug("strategy env 조회 실패 (%s): %s", strategy_id, e)
    return merged


def get_env_from_db(key: str, default: str = "") -> str:
    """config_* + env_config 병합 스냅샷 → 키 값. 없으면 os.environ → default."""
    merged = get_merged_env_dict()
    if merged:
        v = _strip_comment(merged.get(key))
        if v not in (None, ""):
            return str(v)
    # os.environ 폴백 (기존 호환)
    return os.environ.get(key, str(default))


def get_env_float(key: str, default: float) -> float:
    raw = get_env_from_db(key, str(default))
    try:
        return float(raw) if raw != "" else float(default)
    except (ValueError, TypeError):
        return float(default)


def get_env_int(key: str, default: int) -> int:
    raw = get_env_from_db(key, str(default))
    try:
        # "1.0" 같은 값이 들어와도 int 캐스팅되도록 float 경유
        return int(float(raw)) if raw != "" else int(default)
    except (ValueError, TypeError):
        return int(default)


def get_env_bool(key: str, default: bool = False) -> bool:
    raw = str(get_env_from_db(key, str(default))).strip().lower()
    if raw in ("true", "1", "yes", "y", "on"):
        return True
    if raw in ("false", "0", "no", "n", "off", ""):
        return False
    return bool(default)
