from __future__ import annotations

import os
import boto3
from botocore.config import Config
from fastapi import UploadFile
from typing import Optional


class S3Backend:
    """
    Minimal async-friendly wrapper around boto3 for object storage.

    Environment:
    - STORAGE_PROVIDER=local|s3|minio (default local)
    - AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
    - S3_BUCKET
    - S3_ENDPOINT_URL (for MinIO/custom endpoints)
    - SIGNED_URL_TTL_SEC (default 86400)
    """

    def __init__(
        self,
        *,
        bucket: Optional[str] = None,
        endpoint_url: Optional[str] = None,
        region: Optional[str] = None,
    ):
        self.bucket = bucket or os.getenv("S3_BUCKET") or os.getenv("S3_BUCKET_NAME")
        self.endpoint_url = endpoint_url or os.getenv("S3_ENDPOINT_URL")
        self.region = region or os.getenv("AWS_REGION", "us-east-1")

        # Configure boto3 client; credentials are auto-resolved from env/instance
        self._client = boto3.client(
            "s3",
            region_name=self.region,
            endpoint_url=self.endpoint_url,
            config=Config(s3={"addressing_style": "path"})
            if self.endpoint_url
            else None,
        )

    async def save(self, file: UploadFile, key: str) -> str:
        """Save object to bucket and return s3:// URL."""
        # boto3 is sync; call directly (OK for short uploads). For larger files, consider threadpool.
        body = await file.read()
        self._client.put_object(
            Bucket=self.bucket, Key=key, Body=body, ContentType=file.content_type
        )
        return f"s3://{self.bucket}/{key}"

    async def read(self, key: str) -> bytes:
        obj = self._client.get_object(Bucket=self.bucket, Key=key)
        return obj["Body"].read()

    async def generate_presigned_url(
        self, key: str, expires_in: Optional[int] = None, operation: str = "get_object"
    ) -> str:
        ttl = int(expires_in or os.getenv("SIGNED_URL_TTL_SEC", 86400))
        return self._client.generate_presigned_url(
            ClientMethod=operation,
            Params={"Bucket": self.bucket, "Key": key},
            ExpiresIn=ttl,
        )
