"""Configuration settings for gjalla-precommit."""

import os
from pathlib import Path
from typing import Any

import yaml
from pydantic import BaseModel, Field


def get_config_dir() -> Path:
    """Get the configuration directory path.

    Respects HOME environment variable for testing.
    """
    home = os.environ.get("HOME")
    if home:
        return Path(home) / ".gjalla"
    return Path.home() / ".gjalla"


def get_config_file() -> Path:
    """Get the configuration file path."""
    return get_config_dir() / "config.yaml"


# Aliases for internal use
_get_config_dir = get_config_dir
_get_config_file = get_config_file


# For backward compatibility - these evaluate at access time via __getattr__
# Modules that need the path at import time should use get_config_dir() instead
class _LazyPath:
    """A path-like object that evaluates lazily."""

    def __init__(self, getter):
        self._getter = getter

    def __fspath__(self):
        return str(self._getter())

    def __str__(self):
        return str(self._getter())

    def __repr__(self):
        return f"_LazyPath({self._getter()})"

    def __truediv__(self, other):
        return self._getter() / other

    def __rtruediv__(self, other):
        return Path(other) / self._getter()

    @property
    def exists(self):
        return self._getter().exists

    def mkdir(self, *args, **kwargs):
        return self._getter().mkdir(*args, **kwargs)


# These provide backward compatibility while respecting HOME env var
CONFIG_DIR = _LazyPath(get_config_dir)
CONFIG_FILE = _LazyPath(get_config_file)


class Settings(BaseModel):
    """Application settings loaded from ~/.gjalla/config.yaml with env var overrides."""

    api_key: str = ""
    api_url: str = "https://gjalla.io"
    projects: dict[str, str] = Field(default_factory=dict)  # repo_path -> project_id
    telemetry: bool | None = None
    install_id: str = ""

    @classmethod
    def load(cls) -> "Settings":
        """Load settings from ~/.gjalla/config.yaml, merged with environment variables.

        Environment variable overrides:
        - GJALLA_API_KEY overrides api_key
        - GJALLA_API_URL overrides api_url

        Returns:
            Settings instance with merged configuration.
        """
        data: dict[str, Any] = {}

        # Load from config file if it exists (use function for dynamic HOME)
        config_file = _get_config_file()
        if config_file.exists():
            with open(config_file) as f:
                file_data = yaml.safe_load(f)
                if file_data:
                    data = file_data

        # Create settings from file data
        settings = cls(**data)

        # Override with environment variables
        env_api_key = os.environ.get("GJALLA_API_KEY")
        if env_api_key:
            settings = settings.model_copy(update={"api_key": env_api_key})

        env_api_url = os.environ.get("GJALLA_API_URL")
        if env_api_url:
            settings = settings.model_copy(update={"api_url": env_api_url})

        return settings

    def save(self) -> None:
        """Save settings to ~/.gjalla/config.yaml.

        Creates the ~/.gjalla directory if it doesn't exist.
        """
        # Create directory if missing (use functions for dynamic HOME)
        config_dir = _get_config_dir()
        config_file = _get_config_file()
        config_dir.mkdir(parents=True, exist_ok=True)

        # Save to YAML file
        with open(config_file, "w") as f:
            yaml.dump(self.model_dump(), f, default_flow_style=False)

    def get_project_id(self, repo_path: Path) -> str | None:
        """Lookup project_id for given repo path.

        Args:
            repo_path: Path to the repository.

        Returns:
            Project ID if found, None otherwise.
        """
        # Normalize path to string for lookup
        path_str = str(repo_path.resolve())
        return self.projects.get(path_str)

    def set_project(self, repo_path: Path, project_id: str) -> None:
        """Set project mapping for a repository path.

        Args:
            repo_path: Path to the repository.
            project_id: Project ID to associate with the path.
        """
        # Normalize path to string for storage
        path_str = str(repo_path.resolve())
        self.projects[path_str] = project_id


def get_settings() -> Settings:
    """Get current settings.

    Returns:
        Settings instance loaded from config file and environment.
    """
    return Settings.load()


def get_api_key() -> str | None:
    """Get the API key from settings.

    Returns:
        API key string if configured, None otherwise.
    """
    settings = Settings.load()
    return settings.api_key if settings.api_key else None


def get_receipt_method(config: dict[str, Any]) -> str:
    """Get the receipt storage method from config.

    Args:
        config: Configuration dictionary.

    Returns:
        Receipt method: "trailer" or "notes" (default).
    """
    return config.get("receipt_method", "notes")


def load_config() -> dict[str, Any]:
    """Load configuration from ~/.gjalla/config.yaml.

    Returns:
        Configuration dictionary with api_key, api_url, and projects.
    """
    data: dict[str, Any] = {}
    config_file = _get_config_file()

    if config_file.exists():
        with open(config_file) as f:
            file_data = yaml.safe_load(f)
            if file_data:
                data = file_data

    return data


def save_config(data: dict[str, Any]) -> None:
    """Save configuration data to the config file.

    This is a convenience function for saving arbitrary config data.
    Merges with existing settings if present.

    Args:
        data: Configuration data to save.
    """
    config_dir = _get_config_dir()
    config_file = _get_config_file()

    # Load existing config and merge with new data
    existing = load_config()
    existing.update(data)

    # Create directory if missing
    config_dir.mkdir(parents=True, exist_ok=True)

    # Save to YAML file
    with open(config_file, "w") as f:
        yaml.dump(existing, f, default_flow_style=False)
