import boto3
from django.core.files.storage import Storage
from django.conf import settings
from urllib.parse import urljoin
from botocore.exceptions import ClientError


class S3AWSStorage(Storage):
    """
    Custom Django storage backend for AWS S3 using boto3.
    Compatible with Django 4.2 y 5.1 (LTS).
    """

    def __init__(self, bucket_name=None, aws_region=None):
        # Lee configuración desde settings.py de Django
        self.bucket_name = bucket_name or getattr(settings, "AWS_STORAGE_BUCKET_NAME", None)
        self.aws_region = aws_region or getattr(settings, "AWS_REGION", "us-east-1")

        if not self.bucket_name:
            raise ValueError("AWS_STORAGE_BUCKET_NAME must be set in Django settings.")

        # Cliente boto3 (puede usar IAM roles si no se definen claves)
        self.s3_client = boto3.client(
            "s3",
            region_name=self.aws_region,
            aws_access_key_id=getattr(settings, "AWS_ACCESS_KEY_ID", None),
            aws_secret_access_key=getattr(settings, "AWS_SECRET_ACCESS_KEY", None),
        )

    def _open(self, name, mode="rb"):
        """
        Abre un archivo desde S3 y retorna un stream (Body).
        """
        obj = self.s3_client.get_object(Bucket=self.bucket_name, Key=name)
        return obj["Body"]

    def _save(self, name, content):
        """
        Guarda un archivo en S3 desde un file-like object.
        """
        self.s3_client.upload_fileobj(content, self.bucket_name, name)
        return name

    def exists(self, name):
        """
        Verifica si un archivo existe en el bucket.
        """
        try:
            self.s3_client.head_object(Bucket=self.bucket_name, Key=name)
            return True
        except ClientError as e:
            if e.response["Error"]["Code"] == "404":
                return False
            raise

    def delete(self, name):
        """
        Elimina un archivo en S3.
        """
        self.s3_client.delete_object(Bucket=self.bucket_name, Key=name)

    def url(self, name):
        """
        Retorna la URL pública de un archivo.
        Si el bucket es privado, se recomienda usar presigned URLs.
        """
        base_url = f"https://{self.bucket_name}.s3.{self.aws_region}.amazonaws.com/"
        return urljoin(base_url, name)

    def generate_presigned_url(self, name, expiration=3600):
        """
        Genera una URL firmada temporal para acceder a un objeto privado.
        expiration: tiempo en segundos (default: 1 hora).
        """
        return self.s3_client.generate_presigned_url(
            "get_object",
            Params={"Bucket": self.bucket_name, "Key": name},
            ExpiresIn=expiration,
        )
