from datetime import datetime, timedelta, timezone
from typing import Literal
from urllib.parse import urlparse

import boto3
from botocore.config import Config

from app.settings import settings
from app.utils.logger import get_logger

logger = get_logger(__name__)

AclType = Literal["public-read", "private"]


def _client():
    return boto3.client(
        "s3",
        region_name=settings.DO_SPACES_REGION,
        endpoint_url=settings.DO_SPACES_ENDPOINT,
        aws_access_key_id=settings.DO_SPACES_ACCESS_KEY,
        aws_secret_access_key=settings.DO_SPACES_SECRET_KEY,
        config=Config(s3={"addressing_style": "virtual"}),
    )


def get_file_url(key: str) -> str:
    if not key:
        return ""
    host = urlparse(settings.DO_SPACES_ENDPOINT).netloc
    return f"https://{settings.DO_SPACES_BUCKET}.{host}/{key}"


def upload_buffer(
    key: str,
    data: bytes,
    content_type: str,
    acl: AclType = "public-read",
) -> dict:
    logger.info(
        "Spaces upload started",
        extra={"key": key, "size_kb": round(len(data) / 1024, 2)},
    )
    try:
        cache_control = (
            "private, max-age=0, no-store"
            if acl == "private"
            else "public, max-age=31536000, immutable"
        )
        client = _client()
        client.put_object(
            Bucket=settings.DO_SPACES_BUCKET,
            Key=key,
            Body=data,
            ContentType=content_type,
            ACL=acl,
            CacheControl=cache_control,
        )
        url = get_file_url(key)
        logger.info("Spaces upload successful", extra={"key": key})
        return {"key": key, "url": url}
    except Exception as exc:
        logger.error(
            "Spaces upload failed",
            extra={
                "key": key,
                "error_name": type(exc).__name__,
                "error_message": str(exc),
            },
        )
        raise RuntimeError("Failed to upload file to storage") from exc


def delete_object(key: str) -> None:
    logger.info("Spaces delete started", extra={"key": key})
    try:
        client = _client()
        client.delete_object(Bucket=settings.DO_SPACES_BUCKET, Key=key)
        logger.info("Spaces delete successful", extra={"key": key})
    except Exception as exc:
        logger.error(
            "Spaces delete failed",
            extra={
                "key": key,
                "error_name": type(exc).__name__,
                "error_message": str(exc),
            },
        )
        raise


def get_signed_file_url(key: str, expires_in_seconds: int = 900) -> dict:
    if not key:
        return {"url": "", "signedUrlExpiredAt": "", "signedUrlCreatedAt": ""}
    try:
        client = _client()
        created_at = datetime.now(timezone.utc)
        url = client.generate_presigned_url(
            "get_object",
            Params={"Bucket": settings.DO_SPACES_BUCKET, "Key": key},
            ExpiresIn=expires_in_seconds,
        )
        expires_at = created_at + timedelta(seconds=expires_in_seconds)
        return {
            "url": url,
            "signedUrlCreatedAt": created_at.isoformat(),
            "signedUrlExpiredAt": expires_at.isoformat(),
        }
    except Exception as exc:
        logger.error(
            "Spaces signed URL generation failed",
            extra={
                "key": key,
                "error_name": type(exc).__name__,
                "error_message": str(exc),
            },
        )
        raise
