"""S3/MinIO 客户端：桶初始化、文件上传/下载/URL 生成"""

import logging
from io import BytesIO

import httpx

from app.config import settings

logger = logging.getLogger(__name__)

S3_ENDPOINT = settings.S3_ENDPOINT
S3_ACCESS_KEY = settings.S3_ACCESS_KEY
S3_SECRET_KEY = settings.S3_SECRET_KEY
S3_BUCKET = settings.S3_BUCKET
S3_REGION = settings.S3_REGION
S3_SECURE = settings.S3_SECURE


def _base_url() -> str:
    """MinIO S3 API 基础 URL（不含桶名）。"""
    return S3_ENDPOINT


def _public_base_url() -> str:
    """供 MinIO 浏览器直接访问的 URL（与 internal endpoint 相同）。"""
    return S3_ENDPOINT


def _s3_headers(method: str, path: str, body: bytes | None = None, content_type: str | None = None) -> dict:
    """构造最简单的 S3 REST 请求头（不含完整的 V4 签名——仅用于内部 MinIO）。"""
    headers = {
        "Host": _base_url().replace("http://", "").replace("https://", "").split("/")[0],
    }
    if content_type:
        headers["Content-Type"] = content_type
    if body is not None:
        headers["Content-Length"] = str(len(body))
    return headers


async def ensure_bucket() -> bool:
    """确保 S3 桶存在，不存在则创建。返回 True 表示成功。"""
    bucket_url = f"{_base_url()}/{S3_BUCKET}"
    try:
        async with httpx.AsyncClient(timeout=10.0, auth=(S3_ACCESS_KEY, S3_SECRET_KEY)) as c:
            # HEAD 检查桶是否存在
            r = await c.head(bucket_url)
            if r.status_code == 200:
                return True
            if r.status_code == 404:
                # PUT 创建桶
                r2 = await c.put(bucket_url)
                if r2.status_code in (200, 201, 204):
                    logger.info("s3_bucket_created bucket=%s endpoint=%s", S3_BUCKET, S3_ENDPOINT)
                    return True
                logger.warning("s3_bucket_create_failed bucket=%s status=%s", S3_BUCKET, r2.status_code)
                return False
            logger.warning("s3_bucket_head_failed bucket=%s status=%s", S3_BUCKET, r.status_code)
            return False
    except httpx.RequestError as e:
        logger.warning("s3_bucket_check_error bucket=%s %s", S3_BUCKET, e)
        return False


async def upload_pdf(object_key: str, pdf_bytes: bytes, content_type: str = "application/pdf") -> str | None:
    """上传 PDF 文件到 S3/MinIO。object_key 如 'literature/12345.pdf'。

    返回对象 URL，失败返回 None。
    """
    if not pdf_bytes:
        return None

    url = f"{_base_url()}/{S3_BUCKET}/{object_key}"
    try:
        async with httpx.AsyncClient(timeout=60.0, auth=(S3_ACCESS_KEY, S3_SECRET_KEY)) as c:
            r = await c.put(url, content=pdf_bytes, headers={"Content-Type": content_type})
            if r.status_code in (200, 201, 204):
                logger.info("s3_upload_ok key=%s size=%d", object_key, len(pdf_bytes))
                return url
            logger.warning("s3_upload_failed key=%s status=%s", object_key, r.status_code)
            return None
    except httpx.RequestError as e:
        logger.warning("s3_upload_error key=%s %s", object_key, e)
        return None


async def download_pdf(object_key: str) -> bytes | None:
    """从 S3/MinIO 下载 PDF 文件。返回字节数据，失败/不存在返回 None。"""
    url = f"{_base_url()}/{S3_BUCKET}/{object_key}"
    try:
        async with httpx.AsyncClient(timeout=30.0, auth=(S3_ACCESS_KEY, S3_SECRET_KEY)) as c:
            r = await c.get(url)
            if r.status_code == 200:
                return r.content
            if r.status_code == 404:
                logger.info("s3_download_not_found key=%s", object_key)
                return None
            logger.warning("s3_download_failed key=%s status=%s", object_key, r.status_code)
            return None
    except httpx.RequestError as e:
        logger.warning("s3_download_error key=%s %s", object_key, e)
        return None


def get_presigned_url(object_key: str, expires_in: int = 3600) -> str | None:
    """获取预签名 URL（带有 GET 权限，限时有效）。

    注意：MinIO 需要完整的 V4 签名，当前返回直接 URL + 依赖 MinIO 匿名只读策略。
    若桶策略不允许匿名 GET，前端应通过后端 API 代理下载。
    """
    host = _public_base_url()
    url = f"{host}/{S3_BUCKET}/{object_key}"
    # TODO: 若桶为私有，需要实现 V4 签名或通过后端 API 网关下载
    return url


def get_pdf_key(pmid: int) -> str:
    """按 PMID 生成 S3 object key。"""
    return f"literature/{pmid}.pdf"
