"""
MinIO 存储连接管理
"""
import os
import logging
import uuid
import io
from datetime import datetime, timedelta
from typing import Optional, Tuple, Dict, Any
from minio import Minio

# 导入配置
from app.core.config import config_handler

logger = logging.getLogger(__name__)

_minio_manager = None

class MinioManager:
    """MinIO 管理器"""
    
    def __init__(self):
        self.endpoint = config_handler.get("admin_app", "MINIO_ENDPOINT")
        self.access_key = config_handler.get("admin_app", "MINIO_ACCESS_KEY")
        self.secret_key = config_handler.get("admin_app", "MINIO_SECRET_KEY")
        self.bucket_name = config_handler.get("admin_app", "MINIO_BUCKET_NAME")
        self.secure = config_handler.get_bool("admin_app", "MINIO_USE_SSL", False)
        self.base_path = config_handler.get("admin_app", "MINIO_BASE_PATH", "lqadmin")
        self.file_base_url = config_handler.get("admin_app", "FILE_BASE_URL", f"http://{self.endpoint}/{self.bucket_name}/{self.base_path}")
        
        self._client = None
        logger.info(f"初始化 MinIO 管理器: endpoint={self.endpoint}, bucket={self.bucket_name}, base_path={self.base_path}")
        if not all([self.endpoint, self.access_key, self.secret_key, self.bucket_name]):
            logger.error(f"MinIO 配置不完整: endpoint={self.endpoint}, bucket={self.bucket_name}, access_key={self.access_key[:4] if self.access_key else 'None'}...")

    @property
    def client(self) -> Minio:
        """获取 MinIO 客户端（延迟初始化）"""
        if self._client is None:
            try:
                self._client = Minio(
                    self.endpoint,
                    access_key=self.access_key,
                    secret_key=self.secret_key,
                    secure=self.secure
                )
                # 确保桶存在
                if not self._client.bucket_exists(self.bucket_name):
                    self._client.make_bucket(self.bucket_name)
                logger.info("MinIO 客户端初始化成功")
            except Exception as e:
                logger.error(f"MinIO 客户端初始化失败: {e}")
                raise
        return self._client

    def get_upload_url(self, filename: str, content_type: str, prefix: str = None, expires_minutes: int = 15) -> Dict[str, Any]:
        """生成预签名上传 URL"""
        unique_id = str(uuid.uuid4())
        ext = os.path.splitext(filename)[1]
        
        # 如果提供了 prefix，则使用 prefix，否则默认使用 image
        if prefix:
            # 确保 prefix 不以 / 开头或结尾，方便统一拼接
            folder_path = prefix.strip("/")
        else:
            folder_path = "image"
            
        object_name = f"{self.base_path}/{folder_path}/{unique_id}{ext}"
        
        # 生成预签名 URL (PUT)
        upload_url = self.client.presigned_put_object(
            self.bucket_name,
            object_name,
            expires=timedelta(minutes=expires_minutes)
        )
        
        # 构造访问 URL (相对于 base_path 的路径)
        relative_path = f"/{folder_path}/{unique_id}{ext}"
        full_file_url = f"{self.file_base_url}{relative_path}"
        
        return {
            "upload_url": upload_url,
            "file_url": full_file_url,
            "relative_path": relative_path,
            "object_name": object_name
        }

    def upload_file(self, file_content: bytes, object_name: str, content_type: str = "application/octet-stream") -> str:
        """上传文件内容到 MinIO"""
        self.client.put_object(
            self.bucket_name,
            object_name,
            io.BytesIO(file_content),
            len(file_content),
            content_type=content_type
        )
        # 返回相对路径
        if object_name.startswith(f"{self.base_path}/"):
            return object_name[len(self.base_path):]
        return f"/{object_name}"

    def get_relative_path(self, url: str) -> str:
        """从完整 URL 中提取相对路径"""
        if not url:
            return ""
        if url.startswith(self.file_base_url):
            return url[len(self.file_base_url):]
        return url

    def get_full_url(self, relative_path: str) -> str:
        """将相对路径转换为完整 URL"""
        if not relative_path:
            return ""
        if relative_path.startswith("http"):
            return relative_path
        # 确保以 / 开头
        if not relative_path.startswith("/"):
            relative_path = "/" + relative_path
        return f"{self.file_base_url}{relative_path}"

    def get_object_content(self, file_url: str) -> Optional[str]:
        """根据文件 URL 或相对路径获取内容，支持多种编码以解决乱码问题"""
        try:
            object_name = None
            
            # 1. 尝试从 URL 中提取 object_name
            if file_url.startswith("http"):
                # 优先匹配 file_base_url
                if file_url.startswith(self.file_base_url):
                    relative_path = file_url[len(self.file_base_url):]
                    # 确保相对路径以 / 开头，然后去掉它以构造 object_name
                    if not relative_path.startswith("/"):
                        relative_path = "/" + relative_path
                    object_name = f"{self.base_path}{relative_path}"
                else:
                    # 兼容旧的 URL 格式: http://{endpoint}/{bucket}/{object_name}
                    parts = file_url.split(f"/{self.bucket_name}/")
                    if len(parts) >= 2:
                        object_name = parts[1]
            
            # 2. 如果不是 URL，或者是无法识别的 URL，则视为相对路径
            if not object_name:
                relative_path = file_url
                if not relative_path.startswith("/"):
                    relative_path = "/" + relative_path
                object_name = f"{self.base_path}{relative_path}"
            
            logger.info(f"正在从 MinIO 获取对象内容: bucket={self.bucket_name}, object={object_name}")
            response = self.client.get_object(self.bucket_name, object_name)
            try:
                data = response.read()
            finally:
                response.close()
                response.release_conn()

            # 尝试多种编码
            encodings = ['utf-8', 'gbk', 'utf-8-sig', 'gb18030', 'utf-16']
            for enc in encodings:
                try:
                    return data.decode(enc)
                except UnicodeDecodeError:
                    continue
            
            # 如果都失败了，尝试忽略错误解码（最后手段）
            return data.decode('utf-8', errors='ignore')
        except Exception as e:
            logger.error(f"获取 MinIO 对象内容失败: {e}, URL/Path: {file_url}")
            return None

def get_minio_manager() -> MinioManager:
    """获取 MinIO 管理器单例"""
    global _minio_manager
    if _minio_manager is None:
        _minio_manager = MinioManager()
    return _minio_manager

def init_minio():
    """初始化 MinIO"""
    return get_minio_manager()
