"""Local config storage — reads/writes ~/.config/noisegate/."""

import importlib.resources
import json
from pathlib import Path
from typing import Any, TypedDict

from noisegate.exceptions import ProgramNotFoundError

_CONFIG_DIR = Path.home() / ".config" / "noisegate"
_CONFIG_FILE = _CONFIG_DIR / "config.json"
_POLICY_FILE = _CONFIG_DIR / "policy.yaml"
_PROGRAMS_DIR = _CONFIG_DIR / "programs"


class ProviderConfig(TypedDict):
    id: str
    name: str
    env_var: str | None
    default_model: str


# Ordered list of supported providers for the init wizard.
PROVIDERS: list[ProviderConfig] = [
    {
        "id": "anthropic",
        "name": "Anthropic",
        "env_var": "ANTHROPIC_API_KEY",
        "default_model": "anthropic:claude-sonnet-4-6",
    },
    {
        "id": "openai",
        "name": "OpenAI",
        "env_var": "OPENAI_API_KEY",
        "default_model": "openai:gpt-4o",
    },
    {
        "id": "google-gla",
        "name": "Google Gemini",
        "env_var": "GOOGLE_API_KEY",
        "default_model": "google-gla:gemini-2.0-flash",
    },
    {
        "id": "ollama",
        "name": "Ollama",
        "env_var": None,
        "default_model": "ollama:llama3",
    },
]


def get_config_dir() -> Path:
    _CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    return _CONFIG_DIR


def bootstrap() -> None:
    """Create config directories and seed default files on first run.

    The policy file is always restored from the bundled default if missing.
    """
    get_config_dir()
    _PROGRAMS_DIR.mkdir(parents=True, exist_ok=True)

    data = importlib.resources.files("noisegate.data")

    if not _POLICY_FILE.exists():
        policy_src = data.joinpath("default_policy.yaml")
        _POLICY_FILE.write_text(policy_src.read_text(encoding="utf-8"))


def _read_config() -> dict[str, Any]:
    get_config_dir()
    if not _CONFIG_FILE.exists():
        return {}
    result: dict[str, Any] = json.loads(_CONFIG_FILE.read_text())
    return result


def _write_config(data: dict[str, Any]) -> None:
    get_config_dir()
    _CONFIG_FILE.write_text(json.dumps(data, indent=2))


def get_api_keys() -> dict[str, str]:
    """Return all stored API keys keyed by provider id."""
    result: dict[str, str] = _read_config().get("api_keys", {})
    return result


def save_api_key(provider: str, key: str) -> None:
    """Persist an API key for a provider."""
    config = _read_config()
    config.setdefault("api_keys", {})[provider] = key
    _write_config(config)


def get_provider_model(provider_id: str) -> str | None:
    """Return the configured model for a specific provider, or None if unset."""
    result: str | None = _read_config().get("provider_models", {}).get(provider_id)
    return result


def save_provider_model(provider_id: str, model: str) -> None:
    """Persist the preferred model for a provider."""
    config = _read_config()
    config.setdefault("provider_models", {})[provider_id] = model
    _write_config(config)


def get_configured_model() -> str | None:
    """Return the user-configured model string, or None if not configured."""
    return _read_config().get("default_model")


def save_default_model(model: str) -> None:
    """Persist the default model string."""
    config = _read_config()
    config["default_model"] = model
    _write_config(config)


def get_default_program_name() -> str | None:
    """Return the current default program name, or None if unset."""
    return _read_config().get("default_program")


def set_default_program(name: str) -> None:
    """Set the default program. Raises ProgramNotFoundError if program doesn't exist."""
    if not (_PROGRAMS_DIR / f"{name}.yaml").exists():
        raise ProgramNotFoundError(name)
    config = _read_config()
    config["default_program"] = name
    _write_config(config)
