"""Provide service-layer logic for minio client."""

from minio import Minio
from minio.error import S3Error
from typing import Optional, Dict, Any
from loguru import logger
from io import BytesIO
import os

from app.config.settings import settings
# Keep service responsibilities explicit so downstream behavior stays predictable.



class MinIOClient:
    """Represent the Min I O Client type."""

    def __init__(
        self,
        endpoint: str = None,
        access_key: str = None,
        secret_key: str = None,
        bucket: str = None,
        secure: bool = None
    ):
        """Initialize the Min I O Client instance."""
        self.endpoint = endpoint or settings.minio_endpoint
        self.access_key = access_key or settings.minio_access_key
        self.secret_key = secret_key or settings.minio_secret_key
        self.bucket = bucket or settings.minio_bucket
        self.secure = secure or settings.minio_secure

        self.client: Optional[Minio] = None
        self.connected = False

        logger.info(f"MinIO客户端配置: {self.endpoint}, bucket={self.bucket}")

    def connect(self) -> bool:
        """Handle connect for the Min I O Client instance."""
        try:
            self.client = Minio(
                self.endpoint,
                access_key=self.access_key,
                secret_key=self.secret_key,
                secure=self.secure
            )
            self.connected = True
            logger.success(f"MinIO连接成功: {self.endpoint}")
            return True
        except Exception as e:
            logger.error(f"MinIO连接失败: {e}")
            self.connected = False
            return False

    def ensure_bucket(self) -> bool:
        """Handle ensure bucket for the Min I O Client instance."""
        if not self.connected:
            logger.warning("未连接MinIO，请先调用connect()")
            return False

        try:
            if not self.client.bucket_exists(self.bucket):
                self.client.make_bucket(self.bucket)
                logger.success(f"创建存储桶: {self.bucket}")
            else:
                logger.info(f"存储桶已存在: {self.bucket}")
            return True
        except S3Error as e:
            logger.error(f"存储桶操作失败: {e}")
            return False

    def upload_file(
        self,
        file_path: str,
        object_name: str,
        metadata: Dict[str, Any] = None
    ) -> bool:
        """Handle upload file for the Min I O Client instance."""
        if not self.connected:
            self.connect()
            self.ensure_bucket()

        try:
            file_size = os.stat(file_path).st_size
            content_type = self._get_content_type(file_path)

            with open(file_path, 'rb') as f:
                self.client.put_object(
                    self.bucket,
                    object_name,
                    f,
                    file_size,
                    content_type=content_type,
                    metadata=metadata
                )

            logger.success(f"文件上传成功: {object_name}, 大小={file_size}")
            return True

        except S3Error as e:
            logger.error(f"文件上传失败: {e}")
            return False

    def upload_bytes(
        self,
        data: bytes,
        object_name: str,
        content_type: str = "application/octet-stream",
        metadata: Dict[str, Any] = None
    ) -> bool:
        """Handle upload bytes for the Min I O Client instance."""
        if not self.connected:
            self.connect()
            self.ensure_bucket()

        try:
            data_stream = BytesIO(data)

            # Keep service responsibilities explicit so downstream behavior stays predictable.
            safe_metadata = None
            if metadata:
                safe_metadata = {}
                for key, value in metadata.items():
                    if isinstance(value, str):
                        # Keep service responsibilities explicit so downstream behavior stays predictable.
                        try:
                            value.encode('ascii')
                            safe_metadata[key] = value
                        except UnicodeEncodeError:
                            # Keep service responsibilities explicit so downstream behavior stays predictable.
                            safe_metadata[key] = ""
                    else:
                        safe_metadata[key] = str(value)

            self.client.put_object(
                self.bucket,
                object_name,
                data_stream,
                len(data),
                content_type=content_type,
                metadata=safe_metadata
            )

            logger.success(f"数据上传成功: {object_name}, 大小={len(data)}")
            return True

        except S3Error as e:
            logger.error(f"数据上传失败: {e}")
            return False

    def download_file(
        self,
        object_name: str,
        file_path: str
    ) -> bool:
        """Handle download file for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            self.client.fget_object(
                self.bucket,
                object_name,
                file_path
            )
            logger.success(f"文件下载成功: {object_name} -> {file_path}")
            return True

        except S3Error as e:
            logger.error(f"文件下载失败: {e}")
            return False

    def get_object_url(
        self,
        object_name: str,
        expires: int = 3600
    ) -> Optional[str]:
        """Return object url for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            url = self.client.presigned_get_object(
                self.bucket,
                object_name,
                expires=expires
            )
            return url

        except S3Error as e:
            logger.error(f"获取URL失败: {e}")
            return None

    def get_object_data(self, object_name: str) -> Optional[bytes]:
        """Return object data for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            response = self.client.get_object(self.bucket, object_name)
            data = response.read()
            response.close()
            response.release_conn()
            return data

        except S3Error as e:
            logger.error(f"获取对象数据失败: {e}")
            return None

    def delete_object(self, object_name: str) -> bool:
        """Delete object for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            self.client.remove_object(self.bucket, object_name)
            logger.info(f"对象删除成功: {object_name}")
            return True

        except S3Error as e:
            logger.error(f"对象删除失败: {e}")
            return False

    def list_objects(self, prefix: str = "") -> list:
        """List objects for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            objects = self.client.list_objects(self.bucket, prefix=prefix)
            return [obj.object_name for obj in objects]

        except S3Error as e:
            logger.error(f"列出对象失败: {e}")
            return []

    def object_exists(self, object_name: str) -> bool:
        """Handle object exists for the Min I O Client instance."""
        if not self.connected:
            self.connect()

        try:
            self.client.stat_object(self.bucket, object_name)
            return True

        except S3Error:
            return False

    def _get_content_type(self, file_path: str) -> str:
        """Handle get content type for this module for the Min I O Client instance."""
        ext = os.path.splitext(file_path)[1].lower()
        content_types = {
            '.pdf': 'application/pdf',
            '.doc': 'application/msword',
            '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            '.txt': 'text/plain',
            '.json': 'application/json',
            '.xml': 'application/xml',
        }
        return content_types.get(ext, 'application/octet-stream')

    def close(self):
        """Release the resources held by this component."""
        self.connected = False
        logger.info("MinIO客户端已关闭")


def create_minio_client() -> MinIOClient:
    """Create minio client."""
    client = MinIOClient()
    client.connect()
    client.ensure_bucket()
    return client
