# -*- coding: utf-8 -*-
"""基础设施层 · OSS 对象存储异步封装（aioboto3，兼容阿里云 OSS / MinIO）。

未配置 OSS 时降级为本地上传目录（uploads/），保证无 OSS 也可用。
"""
from __future__ import annotations

import os
from pathlib import Path
from typing import Optional

from .. import config


class OSS:
    """对象存储：上传文件 / 生成访问 URL。配置了 OSS_ENDPOINT 才启用，否则落本地上传目录。"""

    def __init__(self) -> None:
        self._client: Optional[object] = None
        self.enabled = bool(config.OSS_ENDPOINT)
        self.bucket = config.OSS_BUCKET
        self.local_dir = Path(__file__).resolve().parent.parent.parent / "uploads"
        self.local_dir.mkdir(parents=True, exist_ok=True)

    async def _get_client(self):
        if self._client is None:
            import aioboto3

            session = aioboto3.Session(
                aws_access_key_id=config.OSS_ACCESS_KEY,
                aws_secret_access_key=config.OSS_SECRET_KEY,
            )
            self._client = session.client(
                "s3",
                endpoint_url=config.OSS_ENDPOINT,
                region_name="oss-cn-hangzhou",
            )
        return self._client

    async def upload(self, key: str, data: bytes, content_type: str = "application/octet-stream") -> str:
        """上传对象，返回可访问的相对 URL。"""
        if self.enabled:
            client = await self._get_client()
            await client.put_object(Bucket=self.bucket, Key=key, Body=data, ContentType=content_type)
            return f"/oss/{key}"
        # 本地降级
        dest = self.local_dir / key
        dest.parent.mkdir(parents=True, exist_ok=True)
        dest.write_bytes(data)
        return f"/uploads/{key}"

    async def presigned_url(self, key: str, expires: int = 3600) -> str:
        """生成临时访问 URL（OSS 启用时）。"""
        if self.enabled:
            client = await self._get_client()
            return await client.generate_presigned_url(
                "get_object", Params={"Bucket": self.bucket, "Key": key}, ExpiresIn=expires
            )
        return f"/uploads/{key}"

    @staticmethod
    def is_configured() -> bool:
        return bool(config.OSS_ENDPOINT)
