from datetime import timedelta
from functools import partial
from typing import Any

from anyio import to_thread
from minio import Minio
from minio.error import S3Error

from app.config import Settings
from app.errors import ApiError
from app.schemas.clip import Clip


class StorageService:
    def __init__(self, client: Minio, settings: Settings) -> None:
        self.client = client
        self.settings = settings

    async def get_signed_video_url(self, clip: Clip) -> str:
        return await self._signed_url(clip.s3_path)

    async def get_signed_thumbnail_url(self, clip: Clip) -> str:
        if clip.thumbnail_path is None:
            raise ApiError("thumbnail_not_found", "Clip thumbnail does not exist", 404)
        return await self._signed_url(clip.thumbnail_path)

    async def object_exists(self, object_name: str) -> bool:
        try:
            await to_thread.run_sync(partial(self.client.stat_object, self.settings.minio_bucket, object_name))
        except S3Error as exc:
            if exc.code in {"NoSuchKey", "NoSuchObject", "NoSuchBucket"}:
                return False
            raise ApiError("storage_error", "Storage service is unavailable", 503) from exc
        return True

    async def get_metadata(self, object_name: str) -> dict[str, Any]:
        try:
            stat = await to_thread.run_sync(partial(self.client.stat_object, self.settings.minio_bucket, object_name))
        except S3Error as exc:
            raise ApiError("storage_error", "Storage object metadata is unavailable", 503) from exc
        return {"size": stat.size, "etag": stat.etag, "content_type": stat.content_type}

    async def _signed_url(self, object_name: str) -> str:
        if not await self.object_exists(object_name):
            raise ApiError("object_not_found", "Storage object does not exist", 404)
        try:
            return await to_thread.run_sync(
                partial(
                    self.client.presigned_get_object,
                    self.settings.minio_bucket,
                    object_name,
                    expires=timedelta(seconds=self.settings.signed_url_expiration_seconds),
                )
            )
        except S3Error as exc:
            raise ApiError("storage_error", "Could not generate signed URL", 503) from exc
