from functools import lru_cache
from pathlib import Path

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


def _read_secret(path: str | None) -> str | None:
    if not path:
        return None
    try:
        value = Path(path).read_text(encoding="utf-8").strip()
    except OSError:
        return None
    return value or None


class AISettings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    app_env: str = Field(default="development", pattern=r"^(development|test|production)$")
    ai_service_token: str | None = Field(default=None, min_length=32)
    ai_service_token_file: str | None = None
    openai_api_key: str | None = None
    openai_api_key_file: str | None = None
    openai_model: str | None = None
    openai_fallback_model: str | None = None
    openai_timeout_seconds: float = Field(default=30, ge=1, le=120)
    openai_max_output_tokens: int = Field(default=2048, ge=256, le=16_384)

    def read_service_token(self) -> str | None:
        value = _read_secret(self.ai_service_token_file) or self.ai_service_token
        return value if value and len(value) >= 32 else None

    def read_openai_api_key(self) -> str | None:
        value = _read_secret(self.openai_api_key_file) or self.openai_api_key
        if not value or value.lower() in {"unconfigured", "replace-me"}:
            return None
        return value

    def production_configuration_errors(self) -> list[str]:
        if self.app_env != "production":
            return []
        errors: list[str] = []
        if not self.ai_service_token_file or not self.read_service_token():
            errors.append("AI_SERVICE_TOKEN_FILE must reference a valid mounted token")
        if not self.openai_api_key_file or not self.read_openai_api_key():
            errors.append("OPENAI_API_KEY_FILE must reference a configured provider key")
        if not self.openai_model or not self.openai_model.strip():
            errors.append("OPENAI_MODEL must select an owner-approved production model")
        return errors

    def require_production_configuration(self) -> None:
        errors = self.production_configuration_errors()
        if errors:
            raise RuntimeError("Production AI configuration is unsafe: " + "; ".join(errors))


@lru_cache
def get_ai_settings() -> AISettings:
    return AISettings()
