"""mgf-cloud exception types.

mgf-cloud reuses the federation exception vocabulary from
:mod:`mgf.common.exceptions` (``ProviderError``, ``ResourceNotFoundError``,
``ResourceAlreadyExistsError``, ``OperationError``, ``ConfigError``) and adds two
config-shaped errors of its own. Per rule **EH-08**, every typed exception sets
its machine-readable attributes *before* calling ``super().__init__(message)``,
so callers can branch on ``.attr`` without parsing the message string.
"""

from __future__ import annotations

from mgf.common.exceptions import (
    ConfigError,
    OperationError,
    ProviderError,
    ResourceAlreadyExistsError,
    ResourceNotFoundError,
)

__all__ = [
    "ConfigError",
    "CredentialsError",
    "OperationError",
    "ProviderError",
    "ProviderNotFoundError",
    "ResourceAlreadyExistsError",
    "ResourceNotFoundError",
]


class CredentialsError(ConfigError):
    """Cloud credentials are missing or incomplete.

    Carries the ``source`` they were resolved from (or ``None``) and the tuple
    of ``missing`` required pieces, so a caller (or the CLI) can report exactly
    what to fix without parsing the message.
    """

    def __init__(
        self,
        message: str,
        *,
        source: str | None = None,
        missing: tuple[str, ...] = (),
    ) -> None:
        self.source = source
        self.missing = tuple(missing)
        super().__init__(message)


class ProviderNotFoundError(ConfigError):
    """An unknown cloud-provider name was requested from :func:`mgf.cloud.get_provider`."""

    def __init__(self, provider: str, available: tuple[str, ...]) -> None:
        self.provider = provider
        self.available = tuple(available)
        super().__init__(
            f"unknown cloud provider {provider!r}; available: "
            f"{', '.join(available) if available else '(none)'}"
        )
