# backend/app/runtime_config.py
# 런타임 LLM 설정 오버레이(인메모리). app_setting 테이블 → 프로세스 메모리.
#
# 설계 의도:
#   - .env(Settings)는 ops 가 관리하는 읽기전용 기본값.
#   - 사용자가 설정 페이지에서 바꾸는 LLM 연결만 이 오버레이가 흡수해 즉시 반영한다.
#   - 오버레이가 비어 있으면(기본) effective_* 는 Settings 값과 100% 동일 → 데모/테스트 무손상.
#   - 테스트는 lifespan 을 띄우지 않으므로 load_from_db 가 호출되지 않아 항상 비어 있다.
#     _test/reset 은 clear() 를 호출해 같은 프로세스 내 잔여 오버레이를 비운다.
from dataclasses import dataclass

from .config import get_settings

# 허용된 LLM 오버레이 키(이 외의 키는 무시).
LLM_KEYS = ("llm.provider", "llm.model", "llm.host", "llm.timeout")
_PROVIDERS = ("ollama",)  # 실 LLM 전용(auto/heuristic 제거, phase-16+)

# 인메모리 오버레이. key → str value.
_overrides: dict[str, str] = {}


@dataclass(frozen=True)
class EffectiveLLM:
    provider: str  # auto | ollama | heuristic
    model: str
    host: str
    timeout: float


def clear() -> None:
    """오버레이 비우기 → effective_* 가 .env 기본값으로 복귀."""
    _overrides.clear()


def snapshot() -> dict[str, str]:
    """현재 오버레이의 사본(디버그/응답용)."""
    return dict(_overrides)


def set_llm(values: dict[str, object]) -> None:
    """LLM 오버레이 갱신. None/빈 값은 '기본으로 되돌리기'로 간주해 키를 제거한다."""
    for k, v in values.items():
        if k not in LLM_KEYS:
            continue
        if v is None or (isinstance(v, str) and v.strip() == ""):
            _overrides.pop(k, None)
        else:
            _overrides[k] = str(v)


def load_from_db(session) -> None:
    """앱 시작 시(lifespan) app_setting → 오버레이 적재."""
    from sqlmodel import select

    from .models import AppSetting

    _overrides.clear()
    for row in session.exec(select(AppSetting)).all():
        if row.key in LLM_KEYS:
            _overrides[row.key] = row.value


def effective_llm() -> EffectiveLLM:
    """오버레이 > .env 우선순위로 합성한 유효 LLM 설정."""
    st = get_settings()
    provider = _overrides.get("llm.provider") or st.llm_provider
    if provider not in _PROVIDERS:
        provider = "ollama"  # 미지원/구 값(auto·heuristic) → 실 LLM 으로 강제
    model = _overrides.get("llm.model") or st.ollama_model
    host = _overrides.get("llm.host") or st.ollama_host
    raw_timeout = _overrides.get("llm.timeout")
    try:
        timeout = float(raw_timeout) if raw_timeout else st.llm_timeout
    except ValueError:
        timeout = st.llm_timeout
    return EffectiveLLM(provider=provider, model=model, host=host, timeout=timeout)
