"""
腾讯云COS存储客户端模块

提供腾讯云对象存储服务的封装，支持：
- 文件上传/下载/删除
- 文件列表查询
- 配置文件方式初始化
"""
import os
import logging
from typing import Optional, List, Dict, Any

from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client

from xsdk.file_util import FileUtil
from xsdk.config_reader import ConfigReader

logger = logging.getLogger(__name__)


class LocalCosConfig:
    """
    COS本地配置类

    存储腾讯云COS服务的连接配置信息。

    Attributes:
        secret_id: 腾讯云SecretId
        secret_key: 腾讯云SecretKey
        region: 存储桶所在地域
        bucket: 存储桶名称
        scheme: 协议类型（http/https）
        token: 临时密钥token（可选）
    """

    def __init__(
        self,
        secret_id: str,
        secret_key: str,
        region: str,
        bucket: str,
        scheme: str = "https",
        token: Optional[str] = None
    ):
        self.secret_id = secret_id
        self.secret_key = secret_key
        self.region = region
        self.bucket = bucket
        self.scheme = scheme
        self.token = token

    @staticmethod
    def get_config(conf_file: str, conf_section: str = "cos") -> "LocalCosConfig":
        """
        从配置文件读取COS配置

        Args:
            conf_file: 配置文件路径
            conf_section: 配置节名称

        Returns:
            LocalCosConfig实例
        """
        config_reader = ConfigReader.get_instance(conf_file)
        secret_id = config_reader.get(conf_section, "secret_id")
        secret_key = config_reader.get(conf_section, "secret_key")
        region = config_reader.get(conf_section, "region")
        bucket = config_reader.get(conf_section, "bucket")
        scheme = config_reader.get(conf_section, "scheme") or "https"
        token = config_reader.get(conf_section, "token")

        return LocalCosConfig(secret_id, secret_key, region, bucket, scheme, token)


class CosClient:
    """
    腾讯云COS客户端封装类

    提供文件的上传、下载、删除和列表查询功能。

    Attributes:
        bucket: 存储桶名称
        config: COS配置对象
        client: COS客户端实例
        logger: 日志记录器

    Example:
        >>> client = CosClient.get_client("config.ini")
        >>> client.upload_file("remote/path/file.txt", "/local/path/file.txt")
        >>> client.download_file("remote/path/file.txt", "/local/path/file.txt")
    """

    def __init__(
        self,
        bucket: str,
        config: CosConfig,
        client: CosS3Client,
        logger: logging.Logger = logger
    ):
        self.bucket = bucket
        self.config = config
        self.client = client
        self.logger = logger

    @staticmethod
    def get_client(
        conf_file: str,
        conf_section: str = "cos",
        logger: logging.Logger = logger
    ) -> "CosClient":
        """
        从配置文件创建COS客户端

        Args:
            conf_file: 配置文件路径
            conf_section: 配置节名称
            logger: 日志记录器

        Returns:
            CosClient实例
        """
        local_config = LocalCosConfig.get_config(conf_file, conf_section)

        bucket = local_config.bucket
        cos_config = CosConfig(
            Region=local_config.region,
            SecretId=local_config.secret_id,
            SecretKey=local_config.secret_key,
            Token=local_config.token,
            Scheme=local_config.scheme
        )
        client = CosS3Client(cos_config)

        return CosClient(bucket, cos_config, client, logger)

    def upload_file(self, file_cos: str, file_local: str) -> bool:
        """
        上传本地文件到COS

        Args:
            file_cos: COS上的目标路径
            file_local: 本地文件路径

        Returns:
            上传是否成功
        """
        if not os.path.exists(file_local) or not os.path.isfile(file_local):
            self.logger.warning(f"upload_file: invalid local file: {file_local}")
            return False

        try:
            with open(file_local, 'rb') as fp:
                response = self.client.put_object(
                    Bucket=self.bucket,
                    Body=fp,
                    Key=file_cos,
                    StorageClass='STANDARD',
                    EnableMD5=False
                )
                self.logger.info(f"upload_file: {file_cos} | {response}")
            return True
        except Exception as e:
            self.logger.error(f"upload_file failed: {file_cos} | {e}")
            return False

    def download_file(self, file_cos: str, file_local: str, overwrite: bool = False) -> bool:
        """
        从COS下载文件到本地

        Args:
            file_cos: COS上的文件路径
            file_local: 本地目标路径
            overwrite: 是否覆盖已存在的文件

        Returns:
            下载是否成功
        """
        if os.path.exists(file_local):
            if overwrite:
                FileUtil.rm_file(file_local)
            else:
                self.logger.info(f"download_file: file already exists: {file_local}")
                return False

        try:
            response = self.client.get_object(
                Bucket=self.bucket,
                Key=file_cos
            )
            response['Body'].get_stream_to_file(file_local)
            self.logger.info(f"download_file: {file_cos} -> {file_local}")
            return True
        except Exception as e:
            self.logger.error(f"download_file failed: {file_cos} | {e}")
            return False

    def delete_file(self, file_cos: str) -> bool:
        """
        删除COS上的文件

        Args:
            file_cos: COS上的文件路径

        Returns:
            删除是否成功
        """
        try:
            response = self.client.delete_object(
                Bucket=self.bucket,
                Key=file_cos
            )
            self.logger.info(f"delete_file: {file_cos} | {response}")
            return True
        except Exception as e:
            self.logger.error(f"delete_file failed: {file_cos} | {e}")
            return False

    def list_files(self, prefix: str = "") -> List[Dict[str, Any]]:
        """
        列出COS存储桶中的文件

        Args:
            prefix: 文件路径前缀（用于过滤）

        Returns:
            文件信息字典列表
        """
        try:
            response = self.client.list_objects(
                Bucket=self.bucket,
                Prefix=prefix
            )
            if "Contents" in response and isinstance(response["Contents"], list):
                return response["Contents"]
            else:
                self.logger.info(f"list_files: no files found with prefix '{prefix}'")
                return []
        except Exception as e:
            self.logger.error(f"list_files failed: {e}")
            return []


if __name__ == '__main__':
    pass
