"""모델 메타데이터 유틸리티.

실제 사용 중인 모델들의 context window, max output, 기능 지원 여부 등을
정적으로 정의한다. 외부 API 호출 없이 즉시 사용 가능.

별칭 해석 예시:
  claude-opus → claude-opus-4-6
  gpt4o       → gpt-4o
  deepseek-v3 → deepseek-chat
  gemini-pro  → gemini-2.5-pro
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional


@dataclass(frozen=True)
class ModelMetadata:
    """모델 1개에 대한 기술적 메타데이터.

    Attributes:
        context_window: 최대 컨텍스트 길이 (토큰).
        max_output: 최대 출력 토큰 수.
        supports_vision: 이미지/비전 입력 지원 여부.
        supports_tools: 함수 호출/도구 사용 지원 여부.
        provider: 공급자 식별자 ("anthropic", "openai", "google", "deepseek").
    """

    context_window: int
    max_output: int
    supports_vision: bool
    supports_tools: bool
    provider: str


# ---------------------------------------------------------------------------
# 메타데이터 테이블 (compact tuple 형식)
# 컬럼 순서: context_window, max_output, vision, tools, provider
# ---------------------------------------------------------------------------
def _m(ctx: int, out: int, vis: bool, tools: bool, prov: str) -> ModelMetadata:
    return ModelMetadata(ctx, out, vis, tools, prov)


_AT = "anthropic"
_OA = "openai"
_GG = "google"
_DS = "deepseek"

_METADATA_TABLE: dict[str, ModelMetadata] = {
    # Anthropic Claude 4.6 (1M 컨텍스트)
    "claude-opus-4-6": _m(1_000_000, 32_000, True, True, _AT),
    "claude-sonnet-4-6": _m(1_000_000, 64_000, True, True, _AT),
    # Anthropic Claude 3.5
    "claude-3-5-sonnet-20241022": _m(200_000, 8_192, True, True, _AT),
    "claude-3-5-haiku-20241022": _m(200_000, 8_192, True, True, _AT),
    # Anthropic Claude 3 (구형)
    "claude-3-opus-20240229": _m(200_000, 4_096, True, True, _AT),
    "claude-3-haiku-20240307": _m(200_000, 4_096, True, True, _AT),
    # OpenAI
    "gpt-4o": _m(128_000, 16_384, True, True, _OA),
    "gpt-4o-mini": _m(128_000, 16_384, True, True, _OA),
    # Google Gemini
    "gemini-2.5-pro": _m(1_048_576, 65_536, True, True, _GG),
    "gemini-2.5-flash": _m(1_048_576, 65_536, True, True, _GG),
    "gemini-2.0-flash": _m(1_048_576, 8_192, True, True, _GG),
    # DeepSeek
    "deepseek-chat": _m(64_000, 8_192, False, True, _DS),
    "deepseek-reasoner": _m(64_000, 32_000, False, False, _DS),
}

# ---------------------------------------------------------------------------
# 별칭 테이블: 짧은 이름 / 비공식 이름 → 정식 모델 ID
# ---------------------------------------------------------------------------
_ALIAS_TABLE: dict[str, str] = {
    # Anthropic
    "claude-opus": "claude-opus-4-6",
    "claude-sonnet": "claude-sonnet-4-6",
    "claude-haiku": "claude-3-5-haiku-20241022",
    "claude-3-sonnet": "claude-3-5-sonnet-20241022",
    "opus": "claude-opus-4-6",
    "sonnet": "claude-sonnet-4-6",
    "haiku": "claude-3-5-haiku-20241022",
    # OpenAI
    "gpt4o": "gpt-4o",
    "gpt-4o-latest": "gpt-4o",
    "gpt4o-mini": "gpt-4o-mini",
    # Google
    "gemini-pro": "gemini-2.5-pro",
    "gemini-flash": "gemini-2.5-flash",
    "gemini-2-pro": "gemini-2.5-pro",
    "gemini-2-flash": "gemini-2.5-flash",
    # DeepSeek
    "deepseek-v3": "deepseek-chat",
    "deepseek": "deepseek-chat",
}


def get_metadata(model_id: str) -> ModelMetadata:
    """모델 ID(또는 별칭)로 메타데이터를 조회한다.

    Args:
        model_id: 정식 모델 ID 또는 별칭.

    Returns:
        ModelMetadata 인스턴스.

    Raises:
        KeyError: 알 수 없는 모델 ID.
    """
    canonical = _ALIAS_TABLE.get(model_id, model_id)
    if canonical not in _METADATA_TABLE:
        raise KeyError(f"알 수 없는 모델: {model_id!r}")
    return _METADATA_TABLE[canonical]


def resolve_model(alias: str) -> str:
    """별칭을 정식 모델 ID로 변환한다.

    알 수 없는 별칭은 입력 그대로 반환 (오류 없음).

    Args:
        alias: 별칭 또는 정식 모델 ID.

    Returns:
        정식 모델 ID. 알 수 없으면 alias 그대로.
    """
    if alias in _METADATA_TABLE:
        return alias
    return _ALIAS_TABLE.get(alias, alias)


def list_models(provider: Optional[str] = None) -> list[str]:
    """등록된 모델 ID 목록을 반환한다 (알파벳 정렬).

    Args:
        provider: 필터할 공급자 이름. None이면 전체 반환.
    """
    if provider is None:
        return sorted(_METADATA_TABLE.keys())
    return sorted(mid for mid, meta in _METADATA_TABLE.items() if meta.provider == provider)
