import json
import csv
import sys
from pathlib import Path
from argparse import Namespace
from ..models import DICOMDirectory
from ..utils import create_upload_config
from ..core import upload_series_metadata, upload_dicom_files
from ..core.series_processor import get_series_info, should_upload_series
from requests import Timeout, RequestException, ConnectionError
from requests.exceptions import HTTPError

def log_print(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


def _classify_error(error: Exception) -> str:
    """
    分类错误类型并返回友好的错误消息
    
    Args:
        error: 异常对象
        
    Returns:
        str: 友好的错误描述
    """
    error_str = str(error).lower()
    error_type = type(error).__name__
    
    # 网络超时错误
    if isinstance(error, Timeout) or "timeout" in error_str or "timed out" in error_str:
        return "网络超时：连接或读取超时，请检查网络连接或稍后重试"
    
    # 连接错误
    if isinstance(error, ConnectionError) or "connection" in error_str or "connect" in error_str:
        return "网络连接失败：无法连接到服务器，请检查网络连接和服务器地址"
    
    # HTTP 错误
    if isinstance(error, HTTPError) or "http" in error_str:
        if "401" in error_str or "unauthorized" in error_str or "未登录" in str(error) or "登录已过期" in str(error):
            return "认证失败：登录已过期或认证信息无效，请重新登录"
        elif "403" in error_str or "forbidden" in error_str:
            return "权限不足：没有权限执行此操作"
        elif "404" in error_str or "not found" in error_str:
            return "资源未找到：请求的资源不存在"
        elif "500" in error_str or "server error" in error_str:
            return "服务器错误：服务器内部错误，请稍后重试"
        else:
            return f"HTTP错误：{str(error)}"
    
    # 请求异常
    if isinstance(error, RequestException):
        return f"网络请求失败：{str(error)}"
    
    # 文件相关错误
    if "file" in error_str and ("not found" in error_str or "不存在" in str(error)):
        return f"文件错误：{str(error)}"
    
    # 其他错误，返回原始错误信息
    return f"上传失败：{str(error)}"

def process_single_series(series,series_count: int,patient_name: str,series_type: int,base_url: str,cookie: str,upload_config: Namespace,api_url,use_series_uid: bool = False) -> dict:
    """
    Process and upload a single DICOM series.

    Args:
        series: DICOM series object
        series_count: Series counter
        patient_name: Patient name
        series_type: Series type
        base_url: Base URL
        cookie: Authentication cookie
        upload_config: Upload configuration
        api_url: API URL for querying
        use_series_uid: Whether to use series UID as patient name

    Returns:
        dict: 包含 'status' 和 'error_reason' 的字典
              status: 'success', 'skipped', 或 'failed'
              error_reason: 如果失败，包含错误原因；否则为 None
    """
    series_info = get_series_info(series)

    # 如果需要使用 series UID，则覆盖 patient_name
    if use_series_uid:
        patient_name = series_info["PatientID"]

    series_desc = (
        f"{series_info['SeriesDescription']} "
        f"({series_info['SliceNum']} 切片)"
    )
    # 简化日志输出，减少IO操作
    log_print(f"\n序列 {series_count}: {series_desc} | Patient: {patient_name}")

    if not should_upload_series(series_info):
        log_print("X 序列不符合上传标准，跳过...")
        return {"status": "skipped", "error_reason": None}

    log_print("* 符合标准，开始上传流程...")

    # Step 1: Upload initial metadata (status 11)
    try:
        # 移除冗余的日志输出
        upload_series_metadata(
            series_info, patient_name, series_type, 11, base_url, cookie, verbose=False
        )

        # 文件上传（已优化为并行上传）
        upload_dicom_files(series, upload_config, verbose=False)
        
        # 上传最终元数据
        upload_series_metadata(
            series_info, patient_name, series_type, 12, base_url, cookie, verbose=False
        )
        return {"status": "success", "error_reason": None}
    except Exception as e:
        error_reason = _classify_error(e)
        log_print(f"\n[错误] 序列 {series_count} 上传失败: {error_reason}\n")
        return {"status": "failed", "error_reason": error_reason}
def update_csv_status(directory_path, series_info, status):
    """
    更新CSV文件的上传状态
    拆分后的目录结构：顶层目录/患者文件夹/序列UID文件夹/[sub_phase_X/]
    CSV文件位于顶层目录，需要根据当前路径层级向上查找
    """
    try:
        dir_path = Path(directory_path)
        
        # 检测最后一级目录名是否像序列UID（包含3个'.'和10个以上数字）
        last_dir_name = dir_path.name
        dot_count = last_dir_name.count('.')
        digit_count = sum(c.isdigit() for c in last_dir_name)
        
        # 检测倒数第二级目录名是否像序列UID（处理sub_phase_X的情况）
        parent_dir_name = dir_path.parent.name if dir_path.parent != dir_path else ""
        parent_dot_count = parent_dir_name.count('.')
        parent_digit_count = sum(c.isdigit() for c in parent_dir_name)
        
        # 判断CSV文件位置：
        # 1. 如果最后一级是sub_phase_X，倒数第二级是序列UID -> 上三级
        # 2. 如果最后一级是序列UID -> 上两级
        # 3. 其他情况 -> 当前目录
        
        csv_path = None
        
        # 情况1: 最后一级是sub_phase_X（以sub_phase_开头）
        if last_dir_name.startswith("sub_phase_"):
            # 倒数第二级应该是序列UID
            if parent_dot_count >= 3 and parent_digit_count >= 10:
                # 上三级目录查找CSV
                csv_path = dir_path.parent.parent.parent / "文件与信息汇总.csv"
                log_print(f"[CSV] 检测到sub_phase文件夹，在上三级目录查找CSV: {csv_path}")
            else:
                # 如果倒数第二级不是序列UID，尝试上两级
                csv_path = dir_path.parent.parent / "文件与信息汇总.csv"
                log_print(f"[CSV] 检测到sub_phase文件夹，在上两级目录查找CSV: {csv_path}")
        
        # 情况2: 最后一级是序列UID
        elif dot_count >= 3 and digit_count >= 10:
            # 上两级目录查找CSV
            csv_path = dir_path.parent.parent / "文件与信息汇总.csv"
            log_print(f"[CSV] 检测到序列UID文件夹，在上两级目录查找CSV: {csv_path}")
        
        # 情况3: 其他情况，在当前目录查找
        else:
            csv_path = dir_path / "文件与信息汇总.csv"
            log_print(f"[CSV] 在当前目录查找CSV: {csv_path}")
        
        # 安全检查：确保csv_path已设置
        if csv_path is None:
            csv_path = dir_path / "文件与信息汇总.csv"
            log_print(f"[CSV] 警告：未检测到标准路径结构，使用当前目录: {csv_path}")
        
        # 如果找到的CSV文件不存在，记录日志（稍后会创建新文件）
        if not csv_path.exists():
            log_print(f"[CSV] CSV文件不存在于 {csv_path}，将创建新文件")
        
        # 确保CSV文件所在目录存在
        csv_path.parent.mkdir(parents=True, exist_ok=True)
        
        log_print(f"[CSV] 准备写入CSV文件: {csv_path}")
        log_print(f"[CSV] SeriesInstanceUID: {series_info.get('SeriesInstanceUID')}")
        log_print(f"[CSV] 状态: {status}")
        
        # Fields required for creation
        fieldnames = [
            "PatientID", "PatientName", "SeriesInstanceUID",
            "NewLocation","PatientAge", "StudyDate", "StudyInstanceUID",
            "imageCount", "上传状态"
        ]

        # Prepare row data
        row_data = {}
        for k in fieldnames:
            if k == "NewLocation":
                row_data[k] = str(directory_path)
            elif k == "上传状态":
                row_data[k] = status
            else:
                val = series_info.get(k)
                row_data[k] = str(val) if val is not None else ""

        if csv_path.exists():
            try:
                rows = []
                updated = False
                existing_fieldnames = []
                
                with open(csv_path, 'r', newline='', encoding='utf-8-sig') as f:
                    reader = csv.DictReader(f)
                    existing_fieldnames = reader.fieldnames if reader.fieldnames else []
                    
                    if "上传状态" not in existing_fieldnames:
                        existing_fieldnames.append("上传状态")
                    
                    for row in reader:
                        # Match by SeriesInstanceUID
                        if row.get("SeriesInstanceUID") == str(series_info.get("SeriesInstanceUID")):
                            row["上传状态"] = status
                            updated = True
                            log_print(f"[CSV] 找到匹配行，更新状态为: {status}")
                        rows.append(row)
                
                if not updated:
                    log_print(f"[CSV] 未找到匹配行，追加新行")
                    # If not found, append. Ensure we respect existing fieldnames
                    for f in fieldnames:
                        if f not in existing_fieldnames:
                            existing_fieldnames.append(f)
                    
                    # Fill missing fields in row_data with empty string if they are in existing_fieldnames
                    # And remove fields from row_data that are not in existing_fieldnames (unless we added them)
                    final_row = {k: "" for k in existing_fieldnames}
                    final_row.update({k: v for k, v in row_data.items() if k in existing_fieldnames})
                    rows.append(final_row)

                with open(csv_path, 'w', newline='', encoding='utf-8-sig') as f:
                    writer = csv.DictWriter(f, fieldnames=existing_fieldnames)
                    writer.writeheader()
                    writer.writerows(rows)
                    
                log_print(f"[CSV] CSV文件更新成功: {csv_path}")
                    
            except Exception as e:
                error_msg = f"更新CSV文件失败: {csv_path}, 错误: {str(e)}"
                log_print(f"[CSV错误] {error_msg}")
                import traceback
                log_print(f"[CSV错误] 详细错误信息:\n{traceback.format_exc()}")
                # 重新抛出异常，让调用者知道失败
                raise Exception(error_msg) from e
        else:
            try:
                log_print(f"[CSV] CSV文件不存在，创建新文件: {csv_path}")
                with open(csv_path, 'w', newline='', encoding='utf-8-sig') as f:
                    writer = csv.DictWriter(f, fieldnames=fieldnames)
                    writer.writeheader()
                    writer.writerow(row_data)
                log_print(f"[CSV] CSV文件创建成功: {csv_path}")
            except Exception as e:
                error_msg = f"创建CSV文件失败: {csv_path}, 错误: {str(e)}"
                log_print(f"[CSV错误] {error_msg}")
                import traceback
                log_print(f"[CSV错误] 详细错误信息:\n{traceback.format_exc()}")
                # 重新抛出异常，让调用者知道失败
                raise Exception(error_msg) from e
    except Exception as e:
        # 捕获所有异常并记录，但不阻止主流程
        error_msg = f"update_csv_status执行失败: {str(e)}"
        log_print(f"[CSV严重错误] {error_msg}")
        import traceback
        log_print(f"[CSV严重错误] 详细错误信息:\n{traceback.format_exc()}")
        # 注意：这里不重新抛出异常，避免影响上传流程
        # 但会记录详细的错误信息以便调试

def upload_for_one_directory(directory_path,DEFAULT_CONFIG,series_type):

    config = DEFAULT_CONFIG

    # Initialize basic parameters
    # 标准化路径格式，支持多种输入格式
    directory = str(Path(directory_path).resolve())
    
    base_url = config['base_url']
    if config['cookie'].startswith("ls="):
        cookie = config['cookie']
    else:
        cookie = "ls=" + config['cookie']
    # series_type = config['series_type']
    series_type = int(series_type)
    patient_name = config.get('patient_name', None)
    use_series_uid = patient_name is None  # 如果 patient_name 未设置，则使用 series UID
    if patient_name is None:
        patient_name = 'default'  # 默认值，会被 series UID 覆盖
    api_url = f"{base_url}/api/v2/getSeriesByStudyInstanceUID"

    # Create upload configuration
    upload_config = create_upload_config(config)

    # Initialize DICOM directory
    log_print(f"扫描 DICOM 目录: {directory}")
    dicom_directory = DICOMDirectory(directory)

    # Get all series
    all_series = list(dicom_directory.get_dicom_series())
    total_series = len(all_series)
    log_print(f"发现 {total_series} 个序列\n")

    # Process each series
    successful_uploads = 0
    skipped_series = 0
    failed_series = 0
    patient_num = []
    error_messages = []  # 收集错误信息
    error_reason = None  # 初始化错误原因
    if len(all_series)>1:
        for series_count, series in enumerate(all_series, start=1):
            series_info = get_series_info(series)
            patient_num.append(series_info["PatientID"])
        dit={"message":f"当前文件夹包含{len(all_series)}个序列，建议将文件夹拆分成为多个文件夹，上传单个文件夹"}
        return {
            "content": [
                {
                "type": "text",
                "text": json.dumps(dit, ensure_ascii=False, indent=2)
                }
            ]
        }
    else:
        series_count=1
        series=all_series[0]

        series_info = get_series_info(series)
        patient_num.append(series_info)
        status = "失败"
        try:
            result = process_single_series(
                    series=series,
                    series_count=series_count,
                    patient_name=patient_name,
                    series_type=series_type,
                    base_url=base_url,
                    cookie=cookie,
                    upload_config=upload_config,
                    api_url=api_url,
                    use_series_uid=use_series_uid
            )
            if result["status"] == "success":
                successful_uploads += 1
                status = "成功"
            elif result["status"] == "skipped":
                skipped_series += 1
                status = "跳过"
            else:
                failed_series += 1
                status = "失败"
                error_reason = result.get("error_reason")
        except Exception as e:
            error_reason = _classify_error(e)
            error_msg = f"序列 {series_count} ({series_info.get('SeriesDescription', 'Unknown')}): {error_reason}"
            log_print(f"\n[错误] 处理序列 {series_count} 时出错: {error_reason}\n")
            error_messages.append(error_msg)
            failed_series += 1
            status = "失败"

        # Update CSV status - 即使上传失败也要记录状态
        try:
            update_csv_status(directory, series_info, status)
        except Exception as csv_error:
            # CSV写入失败不应该影响主流程，但需要记录错误
            log_print(f"\n[警告] CSV状态更新失败，但不影响上传流程: {str(csv_error)}\n")
            import traceback
            log_print(f"[CSV警告] 详细错误信息:\n{traceback.format_exc()}\n")
        
        # 获取 study_uid 和 SeriesInstanceUID（在 else 块内，确保 series_info 可用）
        study_uid = series_info["StudyInstanceUID"]
        SeriesInstanceUID = series_info["SeriesInstanceUID"]

    # 构建返回结果
    dic = {
        "successful_uploads": successful_uploads,
        "totalPatients": 1,
        "patients": f"{patient_num[0]}",
        "view_url": f"{config['base_url']}/study/studylist",
        "directory_path":directory,
        "study_uid":study_uid,
        "SeriesInstanceUID":SeriesInstanceUID,
        "type":series_type,
        "upload_status": status
    }
    
    # 如果上传失败，添加失败原因
    if status == "失败" and error_reason:
        dic["error_reason"] = error_reason
    
    return {
        "content": [
            {
                "type": "text",
                "text": json.dumps(dic, ensure_ascii=False, indent=2)
            }
        ]
    }

