"""CLI configuration management using platformdirs + pydantic-settings."""

from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Any

from platformdirs import PlatformDirs
from pydantic import BaseModel, ConfigDict, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict

from .exceptions import ConfigurationError

APP_NAME = "ctxme"
CONFIG_DIR_ENV = "CTXME_CONFIG_DIR"
CACHE_DIR_ENV = "CTXME_CACHE_DIR"


class CLIConfig(BaseModel):
    """User-configurable settings stored on disk."""

    model_config = ConfigDict(extra="forbid", frozen=True)

    api_base_url: str | None = "https://api.ctx.me"
    default_project: str | None = None
    use_mock_client: bool = False


class CLIEnvOverrides(BaseSettings):
    """Environment overrides using pydantic-settings."""

    model_config = SettingsConfigDict(env_prefix="CLI__", env_file=".env", extra="ignore")

    api_base_url: str | None = None
    default_project: str | None = None
    use_mock_client: bool | None = None

    @field_validator("*", mode="before")
    @classmethod
    def _empty_str_to_none(cls, value: Any) -> Any:
        if value == "":
            return None
        return value


class ConfigStore:
    """Handles loading and writing the CLI configuration file."""

    def __init__(
        self,
        *,
        app_name: str = APP_NAME,
        file_name: str = "config.json",
        base_dir: Path | None = None,
    ) -> None:
        env_dir = os.getenv(CONFIG_DIR_ENV)
        if base_dir:
            directory = base_dir
        elif env_dir:
            directory = Path(env_dir).expanduser()
        else:
            dirs = PlatformDirs(app_name, appauthor="Context Platform")
            directory = Path(dirs.user_config_dir)

        self._path = directory / file_name

    @property
    def path(self) -> Path:
        return self._path

    def load(self) -> CLIConfig:
        """Merge config file with env overrides."""
        file_payload: dict[str, Any] = {}
        if self._path.exists():
            try:
                file_payload = json.loads(self._path.read_text())
            except json.JSONDecodeError as exc:
                raise ConfigurationError(f"Config file {self._path} is invalid JSON.") from exc

        env_payload = CLIEnvOverrides().model_dump(exclude_none=True)
        merged = {**file_payload, **env_payload}
        return CLIConfig.model_validate(merged)

    def save(self, config: CLIConfig) -> CLIConfig:
        """Persist config to disk with secure file permissions."""
        payload = config.model_dump(exclude_none=True)
        self._path.parent.mkdir(parents=True, exist_ok=True)
        serialized = json.dumps(payload, indent=2)
        self._path.write_text(serialized + "\n")
        try:
            self._path.chmod(0o600)
        except OSError:
            # Best-effort on platforms that support POSIX permissions.
            pass
        return config
