from functools import lru_cache
from pathlib import Path

from pydantic import Field, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env",
        env_prefix="KBQA_",
        extra="forbid",
    )

    app_name: str
    debug: bool

    dashscope_api_key: str
    dashscope_base_url: str
    chat_model: str
    embedding_model: str
    embedding_dim: int

    database_url: str
    milvus_uri: str
    raw_data_dir: Path

    chunk_size: int = Field(ge=1)
    chunk_overlap: int = Field(ge=0)
    retrieval_top_k: int = Field(ge=1, le=100)
    max_research_searches: int = Field(ge=1, le=10)
    max_sources: int = Field(ge=1, le=50)
    history_message_limit: int = Field(ge=1, le=200)
    max_upload_mib: int = Field(ge=1, le=1024)
    max_query_length: int = Field(ge=1, le=100_000)
    cors_origins: list[str]

    run_live_tests: bool
    live_test_file: Path

    langsmith_tracing: bool = Field(validation_alias="LANGSMITH_TRACING")
    langsmith_endpoint: str = Field(validation_alias="LANGSMITH_ENDPOINT")
    langsmith_api_key: str = Field(validation_alias="LANGSMITH_API_KEY")
    langsmith_project: str = Field(validation_alias="LANGSMITH_PROJECT")

    @field_validator("chunk_overlap")
    @classmethod
    def overlap_must_be_smaller_than_chunk(cls, value: int, info) -> int:
        chunk_size = info.data.get("chunk_size", 500)
        if value >= chunk_size:
            raise ValueError("chunk_overlap must be smaller than chunk_size")
        return value


@lru_cache
def get_settings() -> Settings:
    return Settings()


def validate_live_settings(settings: Settings) -> None:
    missing: list[str] = []
    if not settings.dashscope_api_key:
        missing.append("KBQA_DASHSCOPE_API_KEY")
    if not settings.dashscope_base_url:
        missing.append("KBQA_DASHSCOPE_BASE_URL")
    if missing:
        joined = ", ".join(missing)
        raise RuntimeError(f"Missing live model configuration: {joined}")
