from __future__ import annotations

import logging

from muleline.storage.base import StorageBackend

log = logging.getLogger(__name__)


class S3Storage(StorageBackend):
    """S3-compatible storage (AWS S3, Cloudflare R2, MinIO, etc.)."""

    def __init__(
        self,
        bucket: str,
        endpoint_url: str = "",
        access_key: str = "",
        secret_key: str = "",
        region: str = "auto",
        prefix: str = "",
    ):
        self.bucket = bucket
        self.prefix = prefix.strip("/")

        try:
            import boto3
        except ImportError:
            raise ImportError("Install boto3 for S3 storage: pip install muleline[s3]")

        kwargs = {"region_name": region}
        if endpoint_url:
            kwargs["endpoint_url"] = endpoint_url
        if access_key and secret_key:
            kwargs["aws_access_key_id"] = access_key
            kwargs["aws_secret_access_key"] = secret_key

        self._client = boto3.client("s3", **kwargs)

    def _key(self, key: str) -> str:
        if self.prefix:
            return f"{self.prefix}/{key}"
        return key

    def save(self, key: str, content: bytes, mime_type: str = "") -> bool:
        try:
            params = {"Bucket": self.bucket, "Key": self._key(key), "Body": content}
            if mime_type:
                params["ContentType"] = mime_type
            self._client.put_object(**params)
            return True
        except Exception as e:
            log.error("S3 save failed for %s: %s", key, e)
            return False

    def read(self, key: str) -> bytes | None:
        try:
            resp = self._client.get_object(Bucket=self.bucket, Key=self._key(key))
            return resp["Body"].read()
        except Exception as e:
            if "NoSuchKey" in type(e).__name__ or "404" in str(getattr(e, 'response', {}).get('Error', {}).get('Code', '')):
                return None
            log.error("S3 read failed for %s: %s", key, e)
            return None

    def delete(self, key: str) -> bool:
        try:
            self._client.delete_object(Bucket=self.bucket, Key=self._key(key))
            return True
        except Exception as e:
            log.error("S3 delete failed for %s: %s", key, e)
            return False

    def exists(self, key: str) -> bool:
        try:
            self._client.head_object(Bucket=self.bucket, Key=self._key(key))
            return True
        except Exception:
            return False

    def url(self, key: str, expires: int = 3600) -> str | None:
        try:
            return self._client.generate_presigned_url(
                "get_object",
                Params={"Bucket": self.bucket, "Key": self._key(key)},
                ExpiresIn=expires,
            )
        except Exception as e:
            log.error("S3 presign failed for %s: %s", key, e)
            return None
