"""S3-compatible client for listing and inspecting objects."""

from __future__ import annotations

from contextlib import asynccontextmanager
from typing import AsyncIterator

import aiobotocore.session


@asynccontextmanager
async def create_s3_client(
    endpoint_url: str | None = None,
    access_key: str | None = None,
    secret_key: str | None = None,
    region: str = "us-east-1",
    profile: str | None = None,
) -> AsyncIterator:
    """Create an async S3 client."""
    session = aiobotocore.session.get_session()
    if profile:
        session.set_config_variable("profile", profile)
    kwargs = {"region_name": region}
    if endpoint_url:
        kwargs["endpoint_url"] = endpoint_url
    if access_key:
        kwargs["aws_access_key_id"] = access_key
    if secret_key:
        kwargs["aws_secret_access_key"] = secret_key
    async with session.create_client("s3", **kwargs) as client:
        yield client


async def list_buckets(client) -> list[dict]:
    """List all buckets."""
    resp = await client.list_buckets()
    return [{"name": b["Name"], "created": b["CreationDate"].isoformat()} for b in resp.get("Buckets", [])]


async def list_objects(
    client,
    bucket: str,
    prefix: str = "",
    continuation_token: str | None = None,
    max_keys: int = 100,
) -> dict:
    """List objects in a bucket with prefix filtering."""
    kwargs = {
        "Bucket": bucket,
        "MaxKeys": max_keys,
    }
    if prefix:
        kwargs["Prefix"] = prefix
    if continuation_token:
        kwargs["ContinuationToken"] = continuation_token

    resp = await client.list_objects_v2(**kwargs)

    objects = []
    for obj in resp.get("Contents", []):
        objects.append({
            "key": obj["Key"],
            "size": obj["Size"],
            "last_modified": obj["LastModified"].isoformat(),
        })

    return {
        "objects": objects,
        "is_truncated": resp.get("IsTruncated", False),
        "next_token": resp.get("NextContinuationToken"),
    }


async def get_object_info(client, bucket: str, key: str) -> dict:
    """Get object metadata and content (text preview for small objects)."""
    head = await client.head_object(Bucket=bucket, Key=key)

    info = {
        "key": key,
        "size": head["ContentLength"],
        "content_type": head.get("ContentType", "unknown"),
        "last_modified": head["LastModified"].isoformat(),
        "etag": head.get("ETag", "").strip('"'),
        "metadata": head.get("Metadata", {}),
        "value": None,
    }

    if head["ContentLength"] < 1_048_576:
        ct = head.get("ContentType", "")
        is_text = any(t in ct for t in ("text", "json", "xml", "yaml", "csv", "javascript", "html"))
        if is_text or key.endswith((".json", ".txt", ".csv", ".xml", ".yaml", ".yml", ".log", ".md", ".html")):
            resp = await client.get_object(Bucket=bucket, Key=key)
            body = await resp["Body"].read()
            try:
                info["value"] = body.decode("utf-8")
            except UnicodeDecodeError:
                info["value"] = f"[binary data, {head['ContentLength']} bytes]"
        else:
            info["value"] = f"[{ct or 'binary'}, {head['ContentLength']} bytes]"
    else:
        info["value"] = f"[large file, {head['ContentLength']} bytes]"

    return info
