from typing import Optional, Iterator
from pathlib import Path

import humanize

from ldnode import logger
from ldnode.loader.schemas.enumerations import SyncStatus
from ldnode.loader.schemas.models import DataSource, SyncMetadata, SyncResult
from ldnode.loader.persistence import LightCOSClient


class COSDataSource(DataSource):
    """ COS 数据源 """
    def __init__(self, cos_client: LightCOSClient, metadata: SyncMetadata, local_path: Path):
        self.cos_client = cos_client
        self.local_path = local_path
        # 确保本地数据目录存在
        self.local_path.mkdir(parents=True, exist_ok=True)
        # 存储文件ETag的元数据文件
        self.metadata = metadata
        
    def _check_file_changed(self, filename: str) -> tuple[bool, Optional[tuple[str, str]]]:
        """
            检查文件是否发生变化
            @param filename: 文件名
            @return: 最新的ETAG
        """
        # 获取最新的ETAG
        if latest_tag := self.cos_client.get_file_etag(filename):
            # 获取当前的ETAG
            current_tag = self.metadata.get_file_etag(filename)
            return current_tag != latest_tag, (current_tag, latest_tag)
        else:
            return False, None
        
    def _log_bytes_transferred(self, operation: str, filename: str, bytes_transferred: int) -> None:
        """
            记录流量统计
            @param operation: 操作
            @param filename: 文件名
            @param bytes_transferred: 下载的字节数
        """
        logger.loader.info(f"流量统计 - {operation} {filename}: 共下载 {humanize.naturalsize(bytes_transferred)}({bytes_transferred:,} 字节)")
        
    def _download_file(self, filename: str, latest_etag: str, local_file_path: Path) -> bool:
        """
            下载文件
            @param filename: 文件名
            @param local_file_path: 本地文件路径
            @return: 是否下载成功
        """
        logger.loader.info(f"开始全量同步文件: {filename}")
        try:
            # 重置流量计数器
            self.cos_client.reset_bytes_transferred()
            # 下载文件
            self.cos_client.download_file(filename, local_file_path)
            # 更新ETag
            self.metadata.set_file_etag(filename, latest_etag)
            # 显示总流量消耗
            self._log_bytes_transferred("总计", filename, self.cos_client.get_bytes_transferred())
            return True
        except Exception as e:
            logger.loader.error(f"全量同步文件失败 {filename}: {e}")
            return False
        
    def _get_file_path(self, filename: str) -> Path:
        """
            获取文件路径
            @param filename: 文件名
            @return: 文件路径
        """
        return self.local_path / filename
    
    def _walk_files(self, remote_path: str) -> Iterator[str]:
        """
            遍历文件
            @param remote_path: 远程路径
            @return: 文件列表
        """
        for filename in self.cos_client.list_objects(remote_path):
            yield filename
        
    def load(self, key: str) -> list[SyncResult]:
        results: list[SyncResult] = []
        for filename in self._walk_files(key):
            try:
                # 获取文件路径
                local_file_path = self._get_file_path(filename)
                # 检查文件是否发生变化
                file_changed, (current_tag, latest_tag) = self._check_file_changed(filename)
                # 如果文件不存在, 则下载文件
                if not local_file_path.exists() or file_changed:
                    # 文件发生变化, 下载文件
                    logger.loader.info(f"文件 {filename} 发生变化，执行全量同步, ETag: {current_tag} => {latest_tag}")
                    self._download_file(filename, latest_tag, local_file_path)
                    results.append(SyncResult(filename, SyncStatus.SUCCESS, latest_tag))
                else:
                    results.append(SyncResult(filename, SyncStatus.NO_CHANGE, latest_tag))
            except Exception as e:
                logger.loader.error(f"下载文件 {filename} 失败: {e}")
                results.append(SyncResult(filename, SyncStatus.FAILED, str(e)))
        return results
