"""纯 S3 数据源（v2.14.10 起替代 S3WithCOSFallbackDataSource）"""

import os
from pathlib import Path

from minio import Minio
from minio.error import S3Error

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


class S3DataSource(DataSource):
    """纯 S3 数据源，通过 etag 比对实现增量同步"""

    def __init__(self, minio_client: Minio, bucket: str, metadata: SyncMetadata, local_data_dir: Path):
        self._client = minio_client
        self._bucket = bucket
        self._metadata = metadata
        self._local_dir = local_data_dir
        self._local_dir.mkdir(parents=True, exist_ok=True)

    def _resolve_objects(self, key: str) -> list | None:
        # stat_object 精确匹配（部分 MinIO 版本 list_objects 对精确文件名返回空）
        try:
            self._client.stat_object(self._bucket, key)
            return [type("Obj", (), {"object_name": key})]
        except S3Error:
            pass
        # fallback 到 list_objects（兼容目录型 key）
        objects = list(self._client.list_objects(self._bucket, prefix=key, recursive=True))
        return objects or None

    def load(self, key: str) -> list[SyncResult]:
        results: list[SyncResult] = []
        try:
            objects = self._resolve_objects(key)
            if not objects:
                logger.loader.warning(f"[S3] 文件不存在: {key}")
                return [SyncResult(key, SyncStatus.FAILED, "not found on S3")]
        except Exception as e:
            logger.loader.warning(f"[S3] list_objects 失败 ({key}): {e}")
            return [SyncResult(key, SyncStatus.FAILED, str(e))]

        for obj in objects:
            filename = obj.object_name
            local_path = self._local_dir / filename
            try:
                stat = self._client.stat_object(self._bucket, filename)
                s3_etag = stat.etag
                local_etag = self._metadata.get_file_etag(filename)

                if local_path.exists() and local_etag == s3_etag:
                    results.append(SyncResult(filename, SyncStatus.NO_CHANGE, s3_etag))
                    continue

                logger.loader.info(f"[S3] 同步: {filename} etag {local_etag} → {s3_etag}")
                resp = self._client.get_object(self._bucket, filename)
                try:
                    data = resp.read()
                finally:
                    resp.close()
                    resp.release_conn()

                local_path.parent.mkdir(parents=True, exist_ok=True)
                tmp_path = local_path.with_suffix(".tmp")
                tmp_path.write_bytes(data)
                os.rename(tmp_path, local_path)
                self._metadata.set_file_etag(filename, s3_etag)
                logger.loader.info(f"[S3] 同步完成: {filename} size={len(data):,}")
                results.append(SyncResult(filename, SyncStatus.SUCCESS, s3_etag))

            except Exception as e:
                logger.loader.warning(f"[S3] 下载失败 ({filename}): {e}")
                results.append(SyncResult(filename, SyncStatus.FAILED, str(e)))

        return results
