"""Application settings (CONTRACT §6).

Reads the env keys from ``.env.example`` via pydantic-settings. Defaults mirror
that file so the service is runnable out of the box for offline/dev work. This
module may import pydantic — it is *not* part of the pure ``matching`` package.
"""

from __future__ import annotations

from functools import lru_cache
from typing import Literal, Optional

from pydantic_settings import BaseSettings, SettingsConfigDict

from app.matching.models import MatchSettings


class Settings(BaseSettings):
    """Typed view over the process environment / ``.env``."""

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

    # --- Matching tolerances (CONTRACT §6) ---
    match_abs_tolerance: float = 1.00
    match_rel_tolerance: float = 0.005
    match_rounding: int = 2
    unresolved_policy: Literal["amber", "fuzzy"] = "amber"

    # --- Odoo (read-only) ---
    odoo_url: Optional[str] = None
    odoo_db: Optional[str] = None
    odoo_username: Optional[str] = None
    odoo_api_key: Optional[str] = None
    odoo_software_filter: str = "journal:Software"
    odoo_api_style: Literal["jsonrpc", "oca_rest"] = "jsonrpc"

    # --- Model providers ---
    provider_default: Literal["bedrock", "anthropic", "openai"] = "anthropic"
    anthropic_api_key: Optional[str] = None
    openai_api_key: Optional[str] = None
    aws_region: str = "eu-west-1"
    aws_access_key_id: Optional[str] = None
    aws_secret_access_key: Optional[str] = None
    bedrock_model_id: str = "anthropic.claude-3-5-sonnet-20240620-v1:0"
    anthropic_model_id: str = "claude-3-5-sonnet-latest"
    openai_model_id: str = "gpt-4o"

    # --- MySQL (shared) ---
    mysql_host: str = "mysql"
    mysql_port: int = 3306
    mysql_database: Optional[str] = None
    mysql_user: Optional[str] = None
    mysql_password: Optional[str] = None

    # --- Observability ---
    otel_service_name: str = "invoice-match-service"
    otel_traces_exporter: str = "none"
    otel_exporter_otlp_endpoint: Optional[str] = None

    def match_settings(
        self, year: Optional[int] = None, month: Optional[int] = None
    ) -> MatchSettings:
        """Build the pure :class:`MatchSettings` handed to the classifier."""
        return MatchSettings(
            abs_tolerance=self.match_abs_tolerance,
            rel_tolerance=self.match_rel_tolerance,
            rounding=self.match_rounding,
            unresolved_policy=self.unresolved_policy,
            period_year=year,
            period_month=month,
        )

    def model_id_for(self, provider: str) -> str:
        """Resolve the configured model id for a provider."""
        return {
            "bedrock": self.bedrock_model_id,
            "anthropic": self.anthropic_model_id,
            "openai": self.openai_model_id,
        }.get(provider, self.anthropic_model_id)

    @property
    def mysql_configured(self) -> bool:
        return bool(self.mysql_database and self.mysql_user and self.mysql_host)


@lru_cache
def get_settings() -> Settings:
    """Cached settings singleton."""
    return Settings()
