"""Centralised environment-driven settings.

Single source for all os.getenv() reads. Frozen dataclass ensures
immutability; lazy singleton avoids repeated parsing.

See ADR 019, item 10.
"""

from __future__ import annotations

import os
import shutil
import tempfile
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path


def _env_str(key: str, default: str) -> str:
    return os.getenv(key, default).strip()


def _env_int(key: str, default: int) -> int:
    raw = os.getenv(key)
    if raw is None:
        return default
    try:
        return int(raw)
    except ValueError:
        return default


def _env_path(key: str, default: Path | None = None) -> Path | None:
    raw = os.getenv(key)
    if raw:
        return Path(raw)
    return default


@dataclass(frozen=True, slots=True)
class AppSettings:
    """Application settings loaded from environment variables."""

    template_root: Path | None
    cache_dir: Path
    temp_dir: Path | None
    env_mode: str
    log_level: str
    metrics_path: Path | None
    cache_max_bytes: int
    cache_max_items: int

    def cache_subdir(self, name: str) -> Path:
        """Return a cache subdirectory, creating it if needed."""
        d = self.cache_dir / name
        d.mkdir(parents=True, exist_ok=True)
        return d

    @classmethod
    def from_env(cls) -> AppSettings:
        return cls(
            template_root=_env_path("TOKENMOULDS_TEMPLATE_ROOT"),
            cache_dir=_env_path("TOKENMOULDS_CACHE", Path.home() / ".tokenmoulds")
            or Path.home() / ".tokenmoulds",
            temp_dir=_env_path("TOKENMOULDS_TEMP_DIR"),
            env_mode=_env_str("TOKENMOULDS_ENV", "production"),
            log_level=_env_str("TOKENMOULDS_LOG_LEVEL", "INFO"),
            metrics_path=_env_path("TOKENMOULDS_METRICS_PATH"),
            cache_max_bytes=_env_int("TOKENMOULDS_CACHE_BYTES", 50_000_000),
            cache_max_items=_env_int("TOKENMOULDS_CACHE_ITEMS", 1024),
        )


_settings: AppSettings | None = None


def get_settings() -> AppSettings:
    """Return the singleton AppSettings, creating on first call."""
    global _settings
    if _settings is None:
        _settings = AppSettings.from_env()
    return _settings


def reset_settings() -> None:
    """Reset the singleton (for testing)."""
    global _settings
    _settings = None


# -------------------------------------------------------------------------
# Project-rooted temporary directory (ADR 019, item 11)
# -------------------------------------------------------------------------

_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent


@contextmanager
def temporary_directory(prefix: str = "tokenmoulds_") -> Iterator[Path]:
    """Create a temporary directory, cleaned up on exit.

    In test/development mode the directory is created under the project's
    ``tmp/`` folder for easy inspection.  In production mode (or when
    ``TOKENMOULDS_TEMP_DIR`` is set) it uses the configured or system
    temp directory.
    """
    settings = get_settings()

    if settings.temp_dir is not None:
        base = settings.temp_dir
    elif settings.env_mode in ("test", "development"):
        base = _PROJECT_ROOT / "tmp"
    else:
        base = Path(tempfile.gettempdir())

    base.mkdir(parents=True, exist_ok=True)
    tmp = Path(tempfile.mkdtemp(prefix=prefix, dir=base))
    try:
        yield tmp
    finally:
        shutil.rmtree(tmp, ignore_errors=True)
