"""Cloudinary configuration and upload/destroy helpers.

The SDK is configured once at startup (``configure``); upload/destroy raise
``CloudinaryNotConfigured`` if credentials are missing so the router can return a
clean 503 instead of a 500. Kept free of DB imports so it stays a thin adapter.
"""

from __future__ import annotations

import io

import cloudinary
import cloudinary.uploader

from .config import get_settings


class CloudinaryNotConfigured(RuntimeError):
    """Raised when an upload is attempted without Cloudinary credentials."""


def configure() -> bool:
    """Apply credentials to the global Cloudinary config. Returns False if unset."""
    settings = get_settings()
    if not settings.cloudinary_configured:
        return False
    cloudinary.config(
        cloud_name=settings.cloudinary_cloud_name,
        api_key=settings.cloudinary_api_key,
        api_secret=settings.cloudinary_api_secret,
        secure=True,
    )
    return True


def _ensure_configured() -> None:
    if not configure():
        raise CloudinaryNotConfigured(
            "Cloudinary credentials are not set (CLOUDINARY_CLOUD_NAME / "
            "CLOUDINARY_API_KEY / CLOUDINARY_API_SECRET)."
        )


def upload_image(data: bytes, *, folder: str | None = None) -> dict:
    """Upload raw image bytes to Cloudinary and return the API response dict."""
    _ensure_configured()
    settings = get_settings()
    return cloudinary.uploader.upload(
        io.BytesIO(data),
        folder=folder or settings.cloudinary_folder,
        resource_type="image",
        # Strip metadata and cap stored dimensions; delivery transforms handle
        # the rest. f_auto/q_auto are applied per-request on the frontend.
        transformation=[{"width": 2000, "height": 2000, "crop": "limit"}],
    )


def destroy_image(public_id: str) -> None:
    """Delete an asset from Cloudinary. Invalidates the CDN cache too."""
    _ensure_configured()
    cloudinary.uploader.destroy(public_id, resource_type="image", invalidate=True)
