"""Application settings loaded from environment / .env file."""

from __future__ import annotations

from functools import lru_cache
from pathlib import Path

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """Runtime configuration. Override via environment variables or a .env file."""

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False,
        extra="ignore",
    )

    # App
    app_env: str = "local"
    app_host: str = "0.0.0.0"
    app_port: int = 8000
    app_log_level: str = "INFO"
    root_path: str = ""  # For reverse proxy with path prefix (e.g., "/ocr")

    # Storage (Phase 1: local fs)
    storage_local_dir: Path = Path("./storage")

    # OCR
    ocr_lang: str = "latin"
    ocr_use_gpu: bool = False
    ocr_det_model_dir: str | None = None
    ocr_rec_model_dir: str | None = None
    ocr_cls_model_dir: str | None = None
    ocr_max_image_side: int = 2200

    # Preprocessing
    preprocess_target_dpi: int = 300
    preprocess_denoise: bool = True
    preprocess_deskew: bool = True
    preprocess_adaptive_threshold: bool = False

    # Document detection (Phase 2) — applied to IMAGE sources only
    preprocess_detect_document: bool = True
    preprocess_remove_shadow: bool = True
    preprocess_min_quad_area_fraction: float = Field(0.20, ge=0.0, le=1.0)

    # Table extraction (Phase 3) via PaddleOCR PP-Structure
    tables_enabled: bool = True

    # Confidence thresholds (Phase 5 routing)
    confidence_auto_approve: float = Field(0.95, ge=0.0, le=1.0)
    confidence_needs_review: float = Field(0.85, ge=0.0, le=1.0)

    # LLM (Phase 5)
    llm_enabled: bool = False
    llm_provider: str = "ollama"
    llm_model: str = "qwen2.5:1.5b"
    llm_base_url: str = "http://localhost:11434"
    llm_timeout_s: int = 60

    # Async pipeline (Phase 4)
    queue_enabled: bool = False
    redis_url: str = "redis://localhost:6379/0"
    celery_task_default_queue: str = "ocr_sprint"

    # Persistence (Phase 4). Use sqlite for local dev / tests; Postgres for
    # production via docker-compose.
    database_url: str = "sqlite:///./storage/ocr_sprint.sqlite"
    database_echo: bool = False

    # Blob storage (Phase 4). Local filesystem only for the MVP; the
    # `BlobStorage` interface is designed to swap to S3/MinIO without API
    # changes when needed.
    blob_storage_dir: Path = Path("./storage/blobs")
    blob_max_upload_mb: int = 25

    # Auth (Phase 4). Comma-separated list of API keys accepted by the API.
    # Empty string disables auth (only intended for local dev / tests).
    # We use ``str`` rather than ``list[str]`` because pydantic-settings rejects
    # a bare empty string when binding to a list type.
    api_keys: str = ""
    api_key_header: str = "X-API-Key"

    @property
    def api_keys_list(self) -> list[str]:
        return [k.strip() for k in self.api_keys.split(",") if k.strip()]


@lru_cache(maxsize=1)
def get_settings() -> Settings:
    """Cached accessor so settings are loaded once per process."""
    settings = Settings()
    settings.storage_local_dir.mkdir(parents=True, exist_ok=True)
    settings.blob_storage_dir.mkdir(parents=True, exist_ok=True)
    return settings
