import json
import shutil
import csv
import os
import asyncio
import logging
import time
import random
from pathlib import Path
from typing import List, Dict, Tuple, Optional
from concurrent.futures import ThreadPoolExecutor, as_completed
import pydicom.config

# 设置 pydicom 验证模式，减少无效 UID 的警告


from ..cookie.getcookie import logger
from ..models import DICOMDirectory, Series, DICOMInstance
from ..core.series_processor import get_series_info


class DICOMUIDGenerator:
    """DICOM UID 生成器，生成符合 DICOM 标准的 UID"""
    
    @staticmethod
    def generate_uid() -> str:
        """
        生成符合 DICOM 标准的 UID
        格式: <root>.<suffix>
        确保长度不超过 64 字符，包含有效字符等
        """
        return f"1.2.840.10008.12392002.{int(time.time() * 1000000)}.{random.randint(1000, 9999)}"


class FullPhaseSeperatedSeries:
    """全期相拆分后的Series集合"""
    def __init__(self, phase_series_list: List[Series]):
        self.phase_series_list = phase_series_list
        self.phase_num = len(phase_series_list)

    def __repr__(self) -> str:
        return f"FullPhaseSeperatedSeries(phase_num={self.phase_num}, phase_series_list={self.phase_series_list})"

    def __str__(self) -> str:
        return self.__repr__()

    def __getitem__(self, index: int) -> Series:
        return self.phase_series_list[index]

    def __len__(self) -> int:
        return self.phase_num


class FullPhaseSeperater:
    """全期相拆分器"""
    def __init__(self, series: Series) -> None:
        self.series = series

    def get_missing_instance_numbers(self) -> List[int]:
        """获取缺失的实例编号"""
        if not self.series.instance_number_dict:
            return []
        all_instance_numbers = set(
            range(1, max(self.series.instance_number_dict.keys()) + 1)
        )
        loaded_instance_numbers = set(self.series.instance_number_dict.keys())
        return list(all_instance_numbers - loaded_instance_numbers)

    def get_slice_location_dict(self) -> Dict[float, List[int]]:
        """获取按SliceLocation分组的实例编号字典"""
        slice_dict = {}
        for instance_number, instance in self.series.instance_number_dict.items():
            # 使用轻量级方法读取SliceLocation，避免加载完整dataset
            if hasattr(instance, 'get_tag_lightweight'):
                slice_location = instance.get_tag_lightweight('SliceLocation')
            else:
                slice_location = instance.slice_location
            if slice_location is not None:
                slice_dict.setdefault(slice_location, []).append(instance_number)
            # 读取完标签后立即清理dataset缓存
            if hasattr(instance, 'clear_dataset_cache'):
                instance.clear_dataset_cache()
        return {k: sorted(v) for k, v in sorted(slice_dict.items())}

    def get_sub_phase_info(self, slice_dict: Dict[float, List[int]]) -> Tuple[int, int]:
        """获取子期相信息"""
        if not slice_dict:
            # 如果SliceLocation缺失，无法拆分，返回1,1
            logger.warning("SliceLocation缺失，无法进行全期相拆分")
            return 1, 1
        
        sub_phase_num = max(map(len, slice_dict.values()))
        if sub_phase_num <= 1:
            # 如果所有slice都只有1个实例，无法拆分
            logger.warning("SliceLocation分组无效，无法进行全期相拆分")
            return 1, 1
        
        max_length_slice = next(
            k for k, v in slice_dict.items() if len(v) == sub_phase_num
        )
        max_length_array = slice_dict[max_length_slice]
        if len(max_length_array) < 2:
            # 如果只有1个实例，无法拆分
            logger.warning("SliceLocation分组无效，无法进行全期相拆分")
            return 1, 1
        else:
            sub_phase_instance_num = max_length_array[1] - max_length_array[0]
            return sub_phase_num, sub_phase_instance_num

    def categorize_by_sub_phase_info(
        self, sub_phase_instance_num: int, sub_phase_num: int
    ) -> FullPhaseSeperatedSeries:
        """按子期相信息分类（优化：避免复制dataset，只记录索引）"""
        sub_phase_series_array = [[] for _ in range(sub_phase_num)]
        # 收集实例和对应的目标索引，避免立即复制
        instance_mappings = []  # [(instance, target_index, new_instance_number), ...]
        
        for instance_number, instance in self.series.instance_number_dict.items():
            index = (instance_number - 1) // sub_phase_instance_num if sub_phase_instance_num > 0 else 0
            if index >= sub_phase_num:
                index = sub_phase_num - 1
            instance_mappings.append((instance, index))
        
        # 按目标索引分组，并计算每个组内的新实例编号
        for target_index in range(sub_phase_num):
            instances_for_phase = [(inst, idx) for inst, idx in instance_mappings if idx == target_index]
            # 按原始instance_number排序
            instances_for_phase.sort(key=lambda x: x[0].instance_number or 0)
            for new_idx, (instance, _) in enumerate(instances_for_phase, 1):
                # 只复制文件路径，不复制dataset（延迟加载）
                new_instance = DICOMInstance(instance.filepath)
                # 存储新实例编号，稍后使用
                new_instance._new_instance_number = new_idx
                new_instance._target_phase_index = target_index
                sub_phase_series_array[target_index].append(new_instance)
        
        series_list = []
        for sub_phase_series in sub_phase_series_array:
            if sub_phase_series:
                series_list.append(Series(instances=sub_phase_series))
        
        full_phase_seperated_series = FullPhaseSeperatedSeries(series_list)
        return full_phase_seperated_series

    def reset_full_phase_dicom_tags(
        self, full_phase_seperated_series: FullPhaseSeperatedSeries
    ) -> None:
        """
        重置全期相DICOM标签
        
        优化策略：
        1. 批量生成UID，避免重复调用
        2. 延迟修改，只在保存时应用标签修改
        3. 避免读取dataset，提高性能
        
        Args:
            full_phase_seperated_series: 拆分后的Series集合
        """
        # 步骤1: 从原始series获取SeriesDescription（只读取一次，避免重复IO）
        original_series_desc = None
        original_nominal_percentage = None
        original_image_comments = None
        
        if self.series.instances:
            try:
                first_instance = self.series.instances[0]
                if first_instance._dataset is not None:
                    original_series_desc = first_instance.get_tag("SeriesDescription")
                    original_nominal_percentage = first_instance.get_tag("NominalPercentageOfCardiacPhase")
                    original_image_comments = first_instance.get_tag("ImageComments")
            except Exception:
                # 如果读取失败，使用默认值
                pass
        
        # 步骤2: 计算SeriesDescription（只计算一次，所有子文件夹共享）
        series_description = original_series_desc or "FullPhase"
        if original_nominal_percentage is not None:
            series_description = f"{original_nominal_percentage}%_{series_description}"
        elif original_image_comments and "%" in original_image_comments:
            pos_index = original_image_comments.index("%")
            pre_str = "".join(
                filter(str.isdigit, original_image_comments[max(0, pos_index - 4) : pos_index])
            )
            series_description = f"{pre_str}%-{series_description}"
        
        # 步骤3: 为每个子期相预生成所有需要的UID（使用符合DICOM标准的生成器）
        for sub_phase_index, sub_phase_series in enumerate(
            full_phase_seperated_series.phase_series_list
        ):
            # 为每个子期相生成新的SeriesInstanceUID
            new_series_instance_uid = DICOMUIDGenerator.generate_uid()
            # 批量生成该子期相所有实例的SOPInstanceUID
            sop_uids = [DICOMUIDGenerator.generate_uid() for _ in sub_phase_series.instances]
            
            # 步骤4: 存储标签修改信息到实例对象，延迟到保存时再应用（避免立即读取dataset）
            for index, instance in enumerate(sub_phase_series.instances):
                instance._tag_updates = {
                    "SeriesInstanceUID": new_series_instance_uid,  # 新的Series UID
                    "SOPInstanceUID": sop_uids[index],              # 新的SOP Instance UID
                    "InstanceNumber": index + 1,                    # 重新编号
                    "SeriesNumber": sub_phase_index + 1,            # 子期相编号
                    "SeriesDescription": series_description,        # 使用预计算的描述
                }

    def get_full_phase_seperated_series_description(self, instance: DICOMInstance) -> str:
        """获取全期相拆分后的Series描述（优化：缓存结果）"""
        # 如果已经计算过，直接返回
        if hasattr(instance, '_cached_series_description'):
            return instance._cached_series_description
        
        # 使用get_tag而不是get_dicom_tag，避免不必要的dataset加载
        try:
            # 尝试从dataset获取，但不强制加载
            if instance._dataset is not None:
                series_description = instance.get_tag("SeriesDescription") or "FullPhase"
                nominal_percentage_of_cardiac_phase = instance.get_tag("NominalPercentageOfCardiacPhase")
            else:
                # 如果dataset未加载，使用默认值
                series_description = "FullPhase"
                nominal_percentage_of_cardiac_phase = None
        except:
            series_description = "FullPhase"
            nominal_percentage_of_cardiac_phase = None
        
        if nominal_percentage_of_cardiac_phase is not None:
            series_description = (
                f"{nominal_percentage_of_cardiac_phase}%_{series_description}"
            )
        else:
            try:
                image_comments = instance.get_tag("ImageComments") if instance._dataset else None
                if image_comments and "%" in image_comments:
                    pos_index = image_comments.index("%")
                    pre_str = "".join(
                        filter(
                            str.isdigit, image_comments[max(0, pos_index - 4) : pos_index]
                        )
                    )
                    series_description = f"{pre_str}%-{series_description}"
            except:
                pass
        
        # 缓存结果
        instance._cached_series_description = series_description
        return series_description

    def process(self) -> FullPhaseSeperatedSeries:
        """处理全期相拆分（优化：延迟标签修改到保存时）"""
        slice_dict = self.get_slice_location_dict()
        (
            sub_phase_num,
            sub_phase_instance_num,
        ) = self.get_sub_phase_info(slice_dict)
        logger.info(f"Sub Phase Number: {sub_phase_num}")
        full_phase_seperated_series = self.categorize_by_sub_phase_info(
            sub_phase_instance_num, sub_phase_num
        )
        # 只准备标签修改信息，不立即应用（延迟到保存时）
        self.reset_full_phase_dicom_tags(full_phase_seperated_series)
        return full_phase_seperated_series


class FullPhaseSeperatedSeriesFileWriter:
    """全期相拆分后的Series文件写入器"""
    @staticmethod
    def save(
        full_phase_seperated_series: FullPhaseSeperatedSeries, output_folder: str
    ) -> List[str]:
        """保存拆分后的Series到文件夹，返回所有创建的文件夹列表（优化：批量读取和保存）"""
        created_folders = []
        all_save_tasks = []  # 收集所有保存任务
        
        for sub_phase_index, sub_phase_series in enumerate(
            full_phase_seperated_series.phase_series_list
        ):
            sub_phase_folder = os.path.join(
                output_folder, f"sub_phase_{sub_phase_index+1}"
            )
            if not os.path.exists(sub_phase_folder):
                os.makedirs(sub_phase_folder)
            created_folders.append(sub_phase_folder)
            
            # 收集该子文件夹的所有保存任务
            for index, instance in enumerate(sub_phase_series.instances):
                output_path = os.path.join(sub_phase_folder, f"{index+1}.dcm")
                all_save_tasks.append((instance, output_path))
        
        # 优化的保存函数：只在需要时读取dataset，批量应用标签修改
        def save_single_file(instance_and_path):
            """
            保存单个DICOM文件
            
            Args:
                instance_and_path: (instance, output_path) 元组
                
            Returns:
                bool: 保存是否成功
            """
            instance, output_path = instance_and_path
            try:
                # 1. 读取dataset（延迟加载，只在保存时读取）
                dataset = instance.dataset
                
                # 2. 应用标签修改（如果存在预定义的标签更新）
                if hasattr(instance, '_tag_updates'):
                    for tag, value in instance._tag_updates.items():
                        try:
                            # 使用符合DICOM标准的UID生成器生成的UID
                            setattr(dataset, tag, value)
                        except Exception as e:
                            logger.warning(f"设置标签 {tag} 失败: {e}")
                            # 继续处理，不因单个标签失败而中断
                
                # 3. 保存文件（使用 enforce_file_format 替代已弃用的 write_like_original）
                # enforce_file_format=False 表示不强制使用特定文件格式
                dataset.save_as(output_path, enforce_file_format=False)
                
                # 4. 保存后立即清理dataset缓存以释放内存
                if hasattr(instance, 'clear_dataset_cache'):
                    instance.clear_dataset_cache()
                
                return True
            except Exception as e:
                logger.error(f"保存文件失败: {output_path}, 错误: {e}")
                return False
        
        # 使用线程池并行保存（减少线程数以降低内存占用）
        max_save_workers = min(16, (os.cpu_count() or 4) * 2)  # 减少线程数，降低内存占用
        with ThreadPoolExecutor(max_workers=max_save_workers) as executor:
            # 使用submit和as_completed以便更好地处理错误
            futures = [executor.submit(save_single_file, task) for task in all_save_tasks]
            for future in as_completed(futures):
                try:
                    future.result()
                except Exception as e:
                    logger.error(f"保存任务失败: {e}")
        
        # 等待文件系统同步，确保所有文件写入完成
        time.sleep(0.5)  # 短暂等待，确保文件系统同步
        
        return created_folders


def copy_dicom(src_path: str, dest_dir: str) -> Path:
    src = Path(src_path)
    dest_folder = Path(dest_dir)
    if not src.exists():
        raise FileNotFoundError(f"源文件不存在: {src}")
    dest_folder.mkdir(parents=True, exist_ok=True)

    dest = dest_folder / src.name
    if dest.exists():
        stem = src.stem
        suffix = src.suffix
        i = 1
        while True:
            candidate = dest_folder / f"{stem}_copy{i}{suffix}"
            if not candidate.exists():
                dest = candidate
                break
            i += 1

    shutil.copy2(src, dest)
    return dest


def copy_dicom_parallel(file_dest_pairs: list[tuple[str, str]], max_workers: int = None) -> int:
    """
    并行复制多个 DICOM 文件到各自的目标目录，使用多线程加速 IO 操作。
    如果目标文件已存在，则重命名。
    返回成功复制的文件数量。
    """
    if max_workers is None:
        # 减少线程数以降低内存占用（从 os.cpu_count() * 16 降低到 os.cpu_count() * 4）
        max_workers = min(16, (os.cpu_count() or 4) * 4)

    def copy_single(src_path: str, dest_dir: str) -> Path:
        src = Path(src_path)
        dest_folder = Path(dest_dir)
        if not src.exists():
            raise FileNotFoundError(f"源文件不存在: {src}")
        dest_folder.mkdir(parents=True, exist_ok=True)

        dest = dest_folder / src.name
        if dest.exists():
            stem = src.stem
            suffix = src.suffix
            i = 1
            while True:
                candidate = dest_folder / f"{stem}_copy{i}{suffix}"
                if not candidate.exists():
                    dest = candidate
                    break
                i += 1

        shutil.copy2(src, dest)
        return dest

    success_count = 0
    try:
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            # 分批提交任务，避免一次性提交过多任务导致的问题
            futures = []
            for src, dest in file_dest_pairs:
                try:
                    future = executor.submit(copy_single, src, dest)
                    futures.append(future)
                except RuntimeError as e:
                    # 如果解释器正在关闭，捕获异常并记录
                    if "cannot schedule new futures after interpreter shutdown" in str(e):
                        logger.warning(f"解释器正在关闭，停止提交新任务。已提交: {len(futures)}/{len(file_dest_pairs)}")
                        break
                    else:
                        raise
            
            # 处理已完成的任务
            for future in as_completed(futures):
                try:
                    future.result()
                    success_count += 1
                except Exception as e:
                    logger.exception(f"复制失败: {e}")
    except RuntimeError as e:
        if "cannot schedule new futures after interpreter shutdown" in str(e):
            logger.warning("解释器正在关闭，任务执行被中断")
            # 如果已经成功复制了一些文件，返回成功数量
            return success_count
        else:
            raise
    
    # 等待文件系统同步，确保所有文件复制完成
    time.sleep(0.5)  # 短暂等待，确保文件系统同步

    return success_count





def copy_files(dicom_files: List[DICOMInstance], output_folder: str, series_uid: str) -> None:
    """复制文件到输出文件夹"""
    output_folder_path = os.path.join(output_folder, f"{series_uid}")
    os.makedirs(output_folder_path, exist_ok=True)
    for dicom_file in dicom_files:
        filepath = dicom_file.filepath
        filename = os.path.basename(filepath)
        output_file = os.path.join(output_folder_path, filename)
        shutil.copyfile(filepath, output_file)


def separate_series_by_patient(directory_path):
    def count_files_with_retry(folder_path: Path, max_retries: int = 3, retry_delay: float = 0.5) -> int:
        """
        统计文件夹中的文件数量，带重试机制确保文件系统同步
        
        Args:
            folder_path: 文件夹路径
            max_retries: 最大重试次数
            retry_delay: 重试延迟（秒）
            
        Returns:
            文件数量
        """
        for attempt in range(max_retries):
            try:
                if folder_path.exists() and folder_path.is_dir():
                    count = sum(1 for item in folder_path.rglob("*") if item.is_file())
                    # 如果文件数量为0且不是第一次尝试，可能是文件系统未同步，等待后重试
                    if count == 0 and attempt < max_retries - 1:
                        time.sleep(retry_delay)
                        continue
                    return count
            except Exception as e:
                if attempt < max_retries - 1:
                    logger.warning(f"统计文件数失败（尝试 {attempt + 1}/{max_retries}）: {e}，等待后重试...")
                    time.sleep(retry_delay)
                else:
                    logger.error(f"统计文件数失败（已重试 {max_retries} 次）: {e}")
        return 0
    
    base_path = Path(directory_path)
    csv_file = base_path / "文件与信息汇总.csv"

    # 检查是否已存在汇总文件，如果存在则不执行，防止反复执行
    if csv_file.exists():
        logger.info(f"文件 {csv_file} 已存在，跳过执行以防止反复执行")
        return {
            "content": [
                {
                    "type": "text",
                    "text": json.dumps({
                        "message": f"文件 {csv_file} 已存在，跳过执行以防止反复执行",
                        "skipped": True
                    }, ensure_ascii=False)
                }
            ]
        }
   
    dicom_directory = DICOMDirectory(directory_path)
    # 注意：这里仍然需要转换为列表，因为后续代码需要多次遍历
    # 但我们会通过其他优化来减少内存占用
    all_series = list(dicom_directory.get_dicom_series())

    # 变量初始化
    base_path = Path(directory_path)
    sucess_num = 0
    main_dir = []
    processed_pids = set()
    processed_series_uids = set()  # 跟踪已处理的 SeriesInstanceUID，避免重复记录
    series_records = []
    file_dest_pairs = []  # 收集所有文件和目标目录对
    directories_to_create = set()  # 收集需要创建的目录
    series_uid_file_count = {}  # 统计每个 series_uid 实际复制的文件数量
    series_uid_to_info = {}  # 存储每个series_uid的完整信息，用于JSON输出

    # 第一步：并行获取所有series的信息（IO密集型操作，并行可以大幅加速）
    # 优化：使用轻量级方法读取标签，减少内存占用
    def get_info_for_series(series):
        """获取series信息，返回(series对象, info字典)"""
        try:
            # 先尝试从series对象获取series_uid（如果已设置，避免读取文件）
            series_uid = None
            if hasattr(series, 'series_uid') and series.series_uid:
                series_uid = series.series_uid
            
            # 如果无法直接获取，尝试使用轻量级方法从第一个实例获取
            if not series_uid and series.instances:
                try:
                    if hasattr(series, 'get_dicom_tag_lightweight'):
                        series_uid = series.get_dicom_tag_lightweight('SeriesInstanceUID')
                    else:
                        series_uid = series.get_dicom_tag('SeriesInstanceUID')
                except:
                    pass
            
            # 获取完整信息（使用轻量级方法）
            info = get_series_info(series, use_lightweight=True)
            series_uid = info.get("SeriesInstanceUID", f"unknown_{id(series)}")
            
            # 读取完信息后，清理dataset缓存以释放内存
            if series.instances:
                for instance in series.instances[:1]:  # 只清理第一个实例
                    if hasattr(instance, 'clear_dataset_cache'):
                        instance.clear_dataset_cache()
            
            return (series, info, series_uid)
        except Exception as e:
            logger.warning(f"获取series信息失败: {e}")
            return None

    # 并行处理所有series信息获取（减少线程数以降低内存占用）
    series_info_list = []
    # 减少并行线程数：从 os.cpu_count() * 4 降低到 os.cpu_count() * 2，减少同时加载的文件数
    max_info_workers = min(16, (os.cpu_count() or 4) * 2)
    with ThreadPoolExecutor(max_workers=max_info_workers) as executor:
        futures = {executor.submit(get_info_for_series, series): series for series in all_series}
        for future in as_completed(futures):
            result = future.result()
            if result:
                series_info_list.append(result)

    # 第二步：第一遍遍历，统计所有series的文件数量
    series_info_map = {}  # series对象 -> (info, series_uid)
    for series, info, series_uid in series_info_list:
        series_info_map[series] = (info, series_uid)
    
    # 第一遍：统计每个series_uid的总文件数量
    for series in all_series:
        if series not in series_info_map:
            continue
            
        info, series_uid = series_info_map[series]
        
        # 统计当前series的文件数量
        file_count_for_this_series = 0
        for instance in getattr(series, "instances", []):
            src = (
                getattr(instance, "filepath", None)
                or getattr(instance, "file_path", None)
                or getattr(instance, "path", None)
            )
            if src:
                file_count_for_this_series += 1
        
        # 累加每个 series_uid 的文件数量（因为同一个 series_uid 可能分布在多个 series 对象中）
        if series_uid not in series_uid_file_count:
            series_uid_file_count[series_uid] = 0
        series_uid_file_count[series_uid] += file_count_for_this_series
    
    # 第二遍：合并同一个series_uid的所有series对象，然后处理
    series_uid_to_folders = {}  # 存储每个series_uid对应的所有文件夹列表
    full_phase_series_to_save = []  # 存储需要保存的全期相拆分Series信息: (full_phase_seperated_series, output_folder, series_uid, pid, info)
    
    # 按series_uid分组，合并同一个series_uid的所有series对象
    series_uid_to_series_list = {}  # {series_uid: [series1, series2, ...]}
    for series in all_series:
        if series not in series_info_map:
            continue
        info, series_uid = series_info_map[series]
        
        # 如果文件数量 < 100，跳过此series
        if series_uid not in series_uid_file_count or series_uid_file_count[series_uid] < 100:
            continue
        
        if series_uid not in series_uid_to_series_list:
            series_uid_to_series_list[series_uid] = []
        series_uid_to_series_list[series_uid].append(series)
    
    # 处理每个series_uid（合并后的）
    for series_uid, series_list in series_uid_to_series_list.items():
        file_count = series_uid_file_count[series_uid]
        
        # 获取第一个series的信息（所有series应该有相同的患者信息）
        first_series = series_list[0]
        info, _ = series_info_map[first_series]
        pid = info["PatientID"]

        # 处理患者目录
        p_dir = base_path / pid
        
        if pid not in processed_pids:
            main_dir.append(p_dir)
            processed_pids.add(pid)

        # 对于大series，使用全期相拆分；小于400的直接复制
        if file_count >= 400:
            # 合并同一个series_uid的所有series对象
            merged_series = Series(series_uid=series_uid)
            for s in series_list:
                for instance in getattr(s, "instances", []):
                    merged_series.add_instance(instance)
                    # 确保合并时不会保留dataset引用，避免内存占用
                    if hasattr(instance, 'clear_dataset_cache'):
                        instance.clear_dataset_cache()
            
            logger.info(f"Series {series_uid} 合并了 {len(series_list)} 个series对象，总文件数: {file_count}")
            
            # 先检查是否可以拆分（检查SliceLocation）
            seperater = FullPhaseSeperater(merged_series)
            slice_dict = seperater.get_slice_location_dict()
            sub_phase_num, sub_phase_instance_num = seperater.get_sub_phase_info(slice_dict)
            
            # 如果无法拆分（返回1,1），直接复制，不尝试拆分
            if sub_phase_num == 1 and sub_phase_instance_num == 1:
                logger.info(f"Series {series_uid} SliceLocation缺失或无效，无法拆分，直接复制")
                # 直接复制逻辑
                s_dir = p_dir / series_uid
                directories_to_create.add(str(p_dir))
                directories_to_create.add(str(s_dir))
                
                if series_uid not in processed_series_uids:
                    record = {k: str(v) if v is not None else "" for k, v in info.items()}
                    record["NewLocation"] = str(s_dir)
                    series_records.append(record)
                    processed_series_uids.add(series_uid)
                    
                    series_uid_to_info[series_uid] = {
                        "series_uid": series_uid,
                        "folders": [str(s_dir)],
                        "patient_id": pid
                    }
                
                # 收集所有series对象的文件进行复制
                for s in series_list:
                    for instance in getattr(s, "instances", []):
                        src = (
                            getattr(instance, "filepath", None)
                            or getattr(instance, "file_path", None)
                            or getattr(instance, "path", None)
                        )
                        if not src:
                            logger.warning(f"实例缺少路径: patient={pid}, series={series_uid}")
                            continue
                        file_dest_pairs.append((src, str(s_dir)))
            else:
                    # 可以拆分，继续拆分流程
                try:
                    full_phase_seperated_series = seperater.process()
                    
                    # 拆分后立即清理原始merged_series的dataset缓存
                    for instance in merged_series.instances:
                        if hasattr(instance, 'clear_dataset_cache'):
                            instance.clear_dataset_cache()
                    
                    # 验证拆分结果
                    total_split_files = sum(len(phase.instances) for phase in full_phase_seperated_series.phase_series_list)
                    if total_split_files != file_count:
                        logger.warning(f"Series {series_uid} 拆分后文件数不匹配: 原始={file_count}, 拆分后={total_split_files}")
                    
                    # 检查拆分后的子文件夹数量
                    if len(full_phase_seperated_series) <= 1:
                        logger.warning(f"Series {series_uid} 拆分失败，只生成了 {len(full_phase_seperated_series)} 个子文件夹，回退到直接复制")
                        raise ValueError(f"拆分失败，只生成了1个子文件夹")
                    
                    # 创建输出目录
                    s_dir_base = p_dir / series_uid
                    directories_to_create.add(str(p_dir))
                    directories_to_create.add(str(s_dir_base))
                    
                    # 预先创建所有子文件夹
                    for sub_phase_index in range(len(full_phase_seperated_series)):
                        sub_phase_folder = os.path.join(str(s_dir_base), f"sub_phase_{sub_phase_index+1}")
                        directories_to_create.add(sub_phase_folder)
                    
                    # 保存待处理的全期相拆分信息，稍后统一保存
                    full_phase_series_to_save.append((
                        full_phase_seperated_series,
                        str(s_dir_base),
                        series_uid,
                        pid,
                        info
                    ))
                    
                    # 对于拆分的series，不创建主记录，只记录子文件夹（稍后在保存时创建）
                    # 标记为已处理，避免重复
                    if series_uid not in processed_series_uids:
                        processed_series_uids.add(series_uid)
                    
                    logger.info(f"Series {series_uid} 准备进行全期相拆分（文件数: {file_count}，将拆分为 {len(full_phase_seperated_series)} 个子文件夹）")
                    
                except Exception as e:
                    logger.error(f"全期相拆分失败: series_uid={series_uid}, error={e}")
                    logger.info(f"回退到直接复制模式: series_uid={series_uid}")
                    # 如果拆分失败，回退到正常处理
                    s_dir = p_dir / series_uid
                    directories_to_create.add(str(p_dir))
                    directories_to_create.add(str(s_dir))
                    
                    if series_uid not in processed_series_uids:
                        record = {k: str(v) if v is not None else "" for k, v in info.items()}
                        record["NewLocation"] = str(s_dir)
                        series_records.append(record)
                        processed_series_uids.add(series_uid)
                        
                        series_uid_to_info[series_uid] = {
                            "series_uid": series_uid,
                            "folders": [str(s_dir)],
                            "patient_id": pid
                        }

                    # 收集所有series对象的文件进行复制
                    for s in series_list:
                        for instance in getattr(s, "instances", []):
                            src = (
                                getattr(instance, "filepath", None)
                                or getattr(instance, "file_path", None)
                                or getattr(instance, "path", None)
                            )
                            if not src:
                                logger.warning(f"实例缺少路径: patient={pid}, series={series_uid}")
                                continue
                            file_dest_pairs.append((src, str(s_dir)))
        else:
            # 小于500的文件，直接复制
            s_dir = p_dir / series_uid
            directories_to_create.add(str(p_dir))
            directories_to_create.add(str(s_dir))
            
            if series_uid not in processed_series_uids:
                record = {k: str(v) if v is not None else "" for k, v in info.items()}
                record["NewLocation"] = str(s_dir)
                series_records.append(record)
                processed_series_uids.add(series_uid)
                
                series_uid_to_info[series_uid] = {
                    "series_uid": series_uid,
                    "folders": [str(s_dir)],
                    "patient_id": pid
                }

            # 收集所有series对象的文件进行复制
            for s in series_list:
                for instance in getattr(s, "instances", []):
                    src = (
                        getattr(instance, "filepath", None)
                        or getattr(instance, "file_path", None)
                        or getattr(instance, "path", None)
                    )
                    if not src:
                        logger.warning(f"实例缺少路径: patient={pid}, series={series_uid}")
                        continue
                    file_dest_pairs.append((src, str(s_dir)))
                
            logger.info(f"Series {series_uid} 直接复制（文件数: {file_count} < 400）")

    # 批量创建所有目录（并行）
    def create_dir(path_str: str):
        Path(path_str).mkdir(parents=True, exist_ok=True)
    
    if directories_to_create:
        with ThreadPoolExecutor(max_workers=min(32, len(directories_to_create))) as executor:
            list(executor.map(create_dir, directories_to_create))

    # 保存所有全期相拆分的文件，并为每个子文件夹创建CSV记录
    series_uid_to_subfolder_records = {}  # 存储每个series_uid对应的子文件夹CSV记录
    logger.info(f"开始处理 {len(full_phase_series_to_save)} 个需要拆分的series")
    
    for full_phase_seperated_series, output_folder, series_uid, pid, info in full_phase_series_to_save:
        try:
            logger.info(f"正在保存拆分后的Series: {series_uid}, 输出目录: {output_folder}")
            created_folders = FullPhaseSeperatedSeriesFileWriter.save(
                full_phase_seperated_series, output_folder
            )
            logger.info(f"Series {series_uid} 已创建 {len(created_folders)} 个子文件夹: {created_folders}")
            
            # 记录所有创建的文件夹
            if series_uid not in series_uid_to_folders:
                series_uid_to_folders[series_uid] = []
            series_uid_to_folders[series_uid].extend(created_folders)
            
            # 为每个子文件夹创建CSV记录并验证文件数
            subfolder_records = []
            total_files = 0
            max_files_in_subfolder = 0
            subfolder_series_uids = []  # 存储每个子文件夹的新SeriesInstanceUID
            
            for sub_phase_index, sub_phase_series in enumerate(full_phase_seperated_series.phase_series_list):
                sub_folder = created_folders[sub_phase_index]
                logger.info(f"处理子文件夹 {sub_phase_index + 1}/{len(created_folders)}: {sub_folder}")
                
                # 从第一个实例获取新的SeriesInstanceUID（在reset_full_phase_dicom_tags中已设置）
                new_series_uid = None
                if sub_phase_series.instances:
                    first_instance = sub_phase_series.instances[0]
                    if hasattr(first_instance, '_tag_updates') and 'SeriesInstanceUID' in first_instance._tag_updates:
                        new_series_uid = first_instance._tag_updates['SeriesInstanceUID']
                        logger.debug(f"从_tag_updates获取SeriesInstanceUID: {new_series_uid}")
                    else:
                        # 如果_tag_updates不存在，尝试从保存后的文件中读取
                        try:
                            import pydicom
                            first_file = Path(sub_folder) / "1.dcm"
                            if first_file.exists():
                                ds = pydicom.dcmread(str(first_file))
                                new_series_uid = str(ds.SeriesInstanceUID)
                                logger.debug(f"从文件读取SeriesInstanceUID: {new_series_uid}")
                        except Exception as e:
                            logger.warning(f"无法从文件读取SeriesInstanceUID: {e}")
                            # 使用备用方案
                            new_series_uid = f"{series_uid}_sub_phase_{sub_phase_index + 1}"
                
                if not new_series_uid:
                    new_series_uid = f"{series_uid}_sub_phase_{sub_phase_index + 1}"
                    logger.warning(f"使用备用SeriesInstanceUID: {new_series_uid}")
                
                subfolder_series_uids.append(new_series_uid)
                
                # 统计该子文件夹中的文件数量（使用重试机制确保文件系统同步）
                sub_folder_path = Path(sub_folder)
                file_count = count_files_with_retry(sub_folder_path)
                logger.info(f"子文件夹 {sub_folder} 文件数: {file_count}")
                
                total_files += file_count
                max_files_in_subfolder = max(max_files_in_subfolder, file_count)
                
                # 如果某个子文件夹文件数仍然很大，记录警告
                if file_count > 500:
                    logger.warning(f"Series {series_uid} 的子文件夹 {sub_folder} 文件数仍然很大: {file_count}，可能需要进一步拆分")
                
                # 创建子文件夹的CSV记录
                sub_record = {k: str(v) if v is not None else "" for k, v in info.items()}
                sub_record["NewLocation"] = sub_folder
                sub_record["SeriesInstanceUID"] = new_series_uid  # 使用实际生成的新SeriesInstanceUID
                sub_record["imageCount"] = str(file_count)
                sub_record["SliceNum"] = str(file_count)
                subfolder_records.append(sub_record)
                logger.info(f"已创建子文件夹CSV记录: SeriesInstanceUID={new_series_uid}, NewLocation={sub_folder}, imageCount={file_count}")
            
            # 验证拆分结果
            original_count = series_uid_file_count.get(series_uid, 0)
            if total_files != original_count:
                logger.warning(f"Series {series_uid} 拆分后文件数不匹配: 原始={original_count}, 拆分后总计={total_files}")
            
            logger.info(f"Series {series_uid} 已进行全期相拆分: 创建了 {len(created_folders)} 个子文件夹, 总文件数={total_files}, 最大子文件夹文件数={max_files_in_subfolder}, CSV记录数={len(subfolder_records)}")
            
            series_uid_to_subfolder_records[series_uid] = subfolder_records
            logger.info(f"Series {series_uid} 的子文件夹记录已保存到series_uid_to_subfolder_records，共 {len(subfolder_records)} 条记录")
            
            # 保存series信息用于JSON输出，包含所有子文件夹和总文件数
            series_uid_to_info[series_uid] = {
                "series_uid": series_uid,
                "folders": created_folders,  # 存储所有子文件夹路径
                "subfolder_series_uids": subfolder_series_uids,  # 存储每个子文件夹的新SeriesInstanceUID
                "patient_id": pid,
                "imageCount": total_files,  # 设置总文件数，确保JSON输出包含此series
                "subfolder_counts": [int(r["imageCount"]) for r in subfolder_records]  # 每个子文件夹的文件数
            }
        except Exception as e:
            logger.error(f"保存全期相拆分文件失败: series_uid={series_uid}, error={e}", exc_info=True)

    # 并行复制所有文件（只复制非全期相拆分的文件）
    sucess_num = copy_dicom_parallel(file_dest_pairs)
    
    # 等待文件系统同步，确保所有文件操作完成
    time.sleep(0.5)  # 短暂等待，确保文件系统同步

    # 文件复制完成后，从目标文件夹统计实际文件数量，更新 imageCount
    # 同时过滤掉 imageCount < 100 的记录
    filtered_series_records = []
    for record in series_records:
        new_location = record.get("NewLocation", "")
        series_uid = record.get("SeriesInstanceUID", "")
        actual_count = 0
        
        if new_location:
            try:
                s_dir = Path(new_location)
                actual_count = count_files_with_retry(s_dir)
                # 如果统计失败，使用之前统计的数量作为备选
                if actual_count == 0 and series_uid in series_uid_file_count:
                    actual_count = series_uid_file_count[series_uid]
            except Exception as e:
                logger.warning(f"统计文件夹文件数失败: {new_location}, {e}")
                # 出错时使用之前统计的数量作为备选
                if series_uid in series_uid_file_count:
                    actual_count = series_uid_file_count[series_uid]
        
        # 如果实际文件数量 < 100，跳过此记录（不写入CSV）
        if actual_count < 100:
            logger.info(f"Series {series_uid} 实际文件数量 {actual_count} < 100，不写入CSV")
            continue
        
        # 更新 imageCount
        record["imageCount"] = str(actual_count)
        record["SliceNum"] = str(actual_count)
        
        # 更新JSON输出信息
        if series_uid in series_uid_to_info:
            series_uid_to_info[series_uid]["imageCount"] = actual_count
        
        # 如果是全期相拆分的series，不添加主记录（因为会添加子文件夹记录）
        # 否则添加记录
        if series_uid not in series_uid_to_subfolder_records:
            filtered_series_records.append(record)
        # 注意：全期相拆分的series的子文件夹记录会在后面统一添加

    # 添加全期相拆分的子文件夹记录到CSV
    # 注意：所有拆分的子文件夹都应该被记录，不管文件数量是否小于100
    logger.info(f"开始添加全期相拆分的子文件夹记录到CSV，共有 {len(series_uid_to_subfolder_records)} 个拆分的series")
    for series_uid, subfolder_records in series_uid_to_subfolder_records.items():
        logger.info(f"处理Series {series_uid} 的 {len(subfolder_records)} 个子文件夹记录")
        # 所有拆分的子文件夹都应该被记录
        for sub_record in subfolder_records:
            sub_count = int(sub_record.get("imageCount", 0))
            filtered_series_records.append(sub_record)
            logger.debug(f"添加子文件夹记录到CSV: SeriesInstanceUID={sub_record.get('SeriesInstanceUID')}, NewLocation={sub_record.get('NewLocation')}, imageCount={sub_count}")
        logger.info(f"Series {series_uid}: 已添加 {len(subfolder_records)} 条子文件夹记录到CSV")
    
    logger.info(f"所有子文件夹记录处理完成，filtered_series_records 共有 {len(filtered_series_records)} 条记录")

    # 创建CSV文件并保存信息（只保存 imageCount >= 100 的记录）
    if filtered_series_records:
        csv_file = base_path / "文件与信息汇总.csv"

        # 写入指定的字段（移除"是否需要拆分"）
        priority = [
            "PatientID", "PatientName", "SeriesInstanceUID",
            "NewLocation", "PatientAge", "StudyDate", "StudyInstanceUID",
            "imageCount"
        ]

        try:
            with open(csv_file, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=priority, extrasaction='ignore')
                writer.writeheader()
                writer.writerows(filtered_series_records)
            logger.info(f"序列信息已保存至: {csv_file}，共 {len(filtered_series_records)} 条记录")
        except Exception as e:
            logger.error(f"保存序列信息失败: {e}")

    # 准备JSON输出，包含每个series的文件夹信息
    series_details = []
    for series_uid, info in series_uid_to_info.items():
        # 检查是否应该包含此series
        # 对于拆分的series，检查子文件夹总数；对于非拆分的series，检查imageCount
        should_include = False
        total_count = 0
        
        if series_uid in series_uid_to_subfolder_records:
            # 拆分的series：统计所有子文件夹的文件数
            subfolder_records = series_uid_to_subfolder_records[series_uid]
            total_count = sum(int(r.get("imageCount", 0)) for r in subfolder_records)
            should_include = total_count >= 100
        else:
            # 非拆分的series：使用imageCount
            total_count = info.get("imageCount", 0)
            should_include = total_count >= 100
        
        if should_include:
            folders = info.get("folders", [])
            
            # 如果是拆分的series，添加子文件夹的详细信息
            series_detail = {
                "series_uid": series_uid,
                "folders": folders,
                "imageCount": total_count
            }
            
            # 如果是拆分的series，添加子文件夹的SeriesInstanceUID映射
            if series_uid in series_uid_to_subfolder_records:
                subfolder_series_uids = info.get("subfolder_series_uids", [])
                subfolder_mapping = []
                for idx, folder in enumerate(folders):
                    subfolder_mapping.append({
                        "folder": folder,
                        "series_uid": subfolder_series_uids[idx] if idx < len(subfolder_series_uids) else f"{series_uid}_sub_phase_{idx + 1}",
                        "imageCount": info.get("subfolder_counts", [])[idx] if idx < len(info.get("subfolder_counts", [])) else 0
                    })
                series_detail["subfolders"] = subfolder_mapping
            
            series_details.append(series_detail)
    
    message=f"已为 {len(processed_pids)} 位患者分离 {len(filtered_series_records)} 个序列（已过滤 imageCount < 100），成功复制 {sucess_num} 个文件。"
    dic={
        "totalPatients": len(processed_pids),
        "totalSeries": len(filtered_series_records),
        "totalFilesCopied": sucess_num,
        "message": message,
        "seriesDetails": series_details
    }
    return {
            "content": [
                {
                    "type": "text",
                    "text": json.dumps(dic, ensure_ascii=False, indent=2)
                }
            ]
    }