"""MinIO / S3-compatible object-storage helpers.

Provides thin wrappers around boto3 for bucket management, file upload
and presigned-URL generation.

Two clients are maintained:
- ``_client`` — uses the internal Docker endpoint (``MINIO_ENDPOINT``) for
  all server-side operations (upload, head_bucket, create_bucket).
- ``_public_client`` — used **only** for presigned URL generation.  It uses
  ``MINIO_PUBLIC_ENDPOINT`` when set so the resulting URLs are reachable from
  browsers.  If ``MINIO_PUBLIC_ENDPOINT`` is not configured it falls back to
  ``MINIO_ENDPOINT`` (backwards-compatible).
"""

# Import boto3
import boto3

# Import ClientError from botocore.exceptions
from botocore.exceptions import ClientError

# Import settings from app.config
from app.config import settings

# ---------------------------------------------------------------------------
# Shared clients (module-level singletons)
# ---------------------------------------------------------------------------

_scheme = "https" if settings.MINIO_SECURE else "http"

# Internal client — used for uploads and bucket management
_client = boto3.client(
    # Literal argument value
    "s3",
    # Keyword argument: endpoint_url
    endpoint_url=f"{_scheme}://{settings.MINIO_ENDPOINT}",
    # Keyword argument: aws_access_key_id
    aws_access_key_id=settings.MINIO_ACCESS_KEY,
    # Keyword argument: aws_secret_access_key
    aws_secret_access_key=settings.MINIO_SECRET_KEY,
    # Keyword argument: region_name
    region_name="us-east-1",  # MinIO ignores this but boto3 requires it
)

# Public client — used only for presigned URL generation so URLs contain the
# publicly accessible hostname instead of the internal Docker service name.
_public_endpoint = settings.MINIO_PUBLIC_ENDPOINT or settings.MINIO_ENDPOINT
_public_client = boto3.client(
    "s3",
    endpoint_url=f"{_scheme}://{_public_endpoint}",
    aws_access_key_id=settings.MINIO_ACCESS_KEY,
    aws_secret_access_key=settings.MINIO_SECRET_KEY,
    region_name="us-east-1",
)


# ---------------------------------------------------------------------------
# Public helpers
# ---------------------------------------------------------------------------


def ensure_bucket_exists() -> None:
    """Create the evidence bucket if it does not already exist."""
    # Attempt the following; catch errors below
    try:
        # Call _client.head_bucket()
        _client.head_bucket(Bucket=settings.MINIO_BUCKET)
    # Handle ClientError
    except ClientError:
        # Call _client.create_bucket()
        _client.create_bucket(Bucket=settings.MINIO_BUCKET)


# Define function upload_file
def upload_file(content: bytes, key: str) -> str:
    """Upload *content* to the evidence bucket under *key*.

    Returns the key that was written (same as the input).
    """
    # Call _client.put_object()
    _client.put_object(
        # Keyword argument: Bucket
        Bucket=settings.MINIO_BUCKET,
        # Keyword argument: Key
        Key=key,
        # Keyword argument: Body
        Body=content,
    )
    # Return key
    return key


def download_file(key: str) -> bytes:
    """Download *key* from the evidence bucket and return its raw bytes."""
    response = _client.get_object(Bucket=settings.MINIO_BUCKET, Key=key)
    return response["Body"].read()


def get_presigned_url(key: str, expiration: int = 3600) -> str:
    """Return a presigned GET URL for *key* valid for *expiration* seconds.

    Uses ``_public_client`` so the URL contains the publicly accessible
    hostname (``MINIO_PUBLIC_ENDPOINT``) rather than the internal Docker
    service name (``MINIO_ENDPOINT``).
    """
    return _public_client.generate_presigned_url(
        "get_object",
        # Keyword argument: Params
        Params={"Bucket": settings.MINIO_BUCKET, "Key": key},
        # Keyword argument: ExpiresIn
        ExpiresIn=expiration,
    )
