#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
任务编辑API模块
提供任务编辑相关的API接口
"""

import json
from typing import Dict, List, Any, Optional
from fastapi import APIRouter, Query, Path, Body, Request
from pydantic import ValidationError

from routes.model.task_edit_model import (
    TaskBasicInfo,
    TaskBackupRequest, SubTaskListParams,TaskEditRunRequest,
    TaskInputParam, TaskSaveRequest,ParamValueType
)
from services.task_edit_service import TaskEditService
from routes.common_api import format_response, error_response
from utils.logger import get_logger
# 创建路由
router = APIRouter(
    prefix="/api/vwed-task-edit",
    tags=["VWED任务编辑"]
)

# 设置日志
logger = get_logger("app.task_edit_api")

# 自定义错误处理工具函数
def handle_validation_error(e: ValidationError) -> Dict[str, Any]:
    """
    处理Pydantic验证错误，生成详细的错误信息
    
    Args:
        e: ValidationError实例
        
    Returns:
        Dict[str, Any]: 错误信息
    """
    errors = e.errors()
    
    if not errors:
        return {"message": "验证失败，但未提供具体错误信息", "code": 400}
    
    # 获取第一个错误
    error = errors[0]
    error_type = error.get("type", "")
    loc = error.get("loc", [])
    msg = error.get("msg", "验证失败")
    
    # 转换位置为字符串路径
    loc_str = ".".join([str(l) for l in loc])
    
    # 针对枚举错误提供更详细的信息
    if error_type == "type_error.enum" or "enum" in error_type:
        # 如果是参数类型枚举错误
        if ".type" in loc_str and "ParamValueType" in str(e):
            allowed_values = [v.value for v in ParamValueType]
            error_msg = f"参数 '{loc_str}' 验证失败: 值必须是预设的枚举值之一: {', '.join(allowed_values)}"
            return {"message": error_msg, "code": 400}
    
    # 构造通用错误信息
    error_msg = f"参数 '{loc_str}' 验证失败: {msg}"
    return {"message": error_msg, "code": 400}

@router.get("/block")
async def get_blocks():
    """
    获取系统中所有可用的块数据
    用于任务编辑页面左侧的组件面板显示
    """
    try:
        blocks = await TaskEditService.get_blocks()
        return format_response(data=blocks)
    except Exception as e:
        logger.error(f"获取块数据失败: {str(e)}")
        return error_response(message=f"获取块数据失败: {str(e)}", code=500)

@router.get("/source/{id}")
async def get_task_source(id: str = Path(..., description="任务ID")):
    """
    获取指定任务的源码详情数据
    直接返回vwed_taskdef表中的详细定义
    """
    try:
        task_def = await TaskEditService.get_task_source(id)
        if task_def is None:
            return error_response(message="任务不存在", code=404)
        return format_response(data=task_def)
    except Exception as e:
        logger.error(f"获取任务源码详情失败: {str(e)}")
        return error_response(message=f"获取任务源码详情失败: {str(e)}", code=500)

@router.post("/save")
async def save_task_edit(task_def_data: TaskSaveRequest = Body(...)):
    """
    保存任务的编辑数据
    包括任务的详细定义、输入参数配置、组件配置和连接关系等
    
    Args:
        task_def_data: 规范化的任务定义数据，符合TaskSaveRequest模型
        
    Returns:
        任务保存结果，包含ID、版本号和更新时间
    """
    try:
        # 转换为字典，确保数据格式正确
        task_data_dict = {
            "id": task_def_data.id,
            "detail": task_def_data.detail.model_dump()
        }
        
        # 先检查是否有数据变化
        task_id = task_data_dict.get("id")
        change_result = await TaskEditService.check_task_changes(task_id, task_data_dict)
        
        # 如果数据没有变化，直接返回成功，不进行数据库更新
        if not change_result.get("changed", True):
            return format_response(
                data={
                    "id": change_result.get("id"),
                    "version": change_result.get("version"),
                },
                message="数据未发生变化"
            )
        
        # 数据有变化，执行保存操作
        result = await TaskEditService.save_task_edit(task_data_dict)
        if not result.get("success", False):
            logger.error(f"保存任务编辑数据失败: {result.get('message', '保存失败')}")
            return error_response(message=result.get("message", "保存失败"), code=result.get("code", 400))
        return format_response(
            data={
                "id": result.get("id"),
                "version": result.get("version"),
                "updateTime": result.get("updateTime")
            },
            message="保存成功"
        )
    except ValidationError as e:
        # 处理验证错误
        error_info = handle_validation_error(e)
        logger.error(f"验证错误: {error_info['message']}")
        return error_response(message=error_info["message"], code=error_info["code"])
    except Exception as e:
        logger.exception("保存任务编辑数据失败")
        return error_response(message=f"保存任务编辑数据失败: {str(e)}", code=500)

@router.post("/backup/{id}")
async def backup_task(
    id: str = Path(..., description="要备份的任务ID"),
    backup_request: TaskBackupRequest = Body(...)
):
    """
    创建指定任务的备份副本
    系统会从vwed_taskdef表中复制原任务的所有信息
    """
    try:
        result = await TaskEditService.backup_task(id, backup_request)
        if result is None:
            return error_response(message="任务不存在或备份失败", code=404)
        return format_response(data=result, message="备份成功")
    except Exception as e:
        logger.error(f"备份任务失败: {str(e)}")
        return error_response(message=f"备份任务失败: {str(e)}", code=500)

@router.get("/subtasks/list")
async def get_subtasks_list(
    pageNum: int = Query(1, description="页码"),
    pageSize: int = Query(100, description="每页记录数"),
    keyword: Optional[str] = Query(None, description="搜索关键词"),
    exclude_id: str = Query(..., description="要排除的任务ID，必填")
):
    """
    获取可以被引用的子任务列表
    用于子任务组件的配置
    """
    try:
        params = SubTaskListParams(
            pageNum=pageNum,
            pageSize=pageSize,
            keyword=keyword,
            exclude_id=exclude_id
        )
        result = await TaskEditService.get_subtasks_list(params)
        return format_response(data=result)
    except Exception as e:
        logger.error(f"获取子任务列表失败: {str(e)}")
        return error_response(message=f"获取子任务列表失败: {str(e)}", code=500)

@router.post("/run")
async def run_task(request: Request, run_request: TaskEditRunRequest = Body(...)):
    """
    直接执行指定的任务，并传入任务输入参数
    用于在任务编辑页面上直接启动任务
    
    如果任务输入参数中有必填字段，则params中必须包含对应字段，否则会返回400错误：

    """
    try:
        # 获取客户端IP地址
        client_ip = request.client.host if request.client else None
        
        # 获取客户端设备信息 (User-Agent)
        user_agent = request.headers.get("user-agent", "")
        
        # 获取token（用于同步到系统任务）
        tf_api_token = request.headers.get("x-access-token")
        # 构建客户端信息
        client_info = {
            "user_agent": user_agent,
            "headers": dict(request.headers),
            "method": request.method,
            "url": str(request.url)
        }
        client_info_str = json.dumps(client_info, ensure_ascii=False)
        
        # 先获取任务定义，检查输入参数是否满足必填要求
        task_def = await TaskEditService.get_task_source(run_request.taskId)
        if task_def is None:
            return error_response(message="任务不存在", code=404)
        
        # 检查必填参数
        missing_params = []
        if "detail" in task_def and "inputParams" in task_def["detail"]:
            input_params = task_def["detail"]["inputParams"]
            
            # 将请求参数转换为字典格式，方便查找
            params_dict = {}
            if run_request.params:
                for param in run_request.params:
                    params_dict[param.name] = param.defaultValue
            
            # 检查任务定义中的必填参数是否都已提供
            for param in input_params:
                # 如果参数是必填的，但在请求中没有提供
                if param.get("required", False) and (
                    not params_dict or 
                    param["name"] not in params_dict
                ):
                    missing_params.append(param["name"])
            
            # 如果有缺失的必填参数，返回错误
            if missing_params:
                logger.warning(f"任务启动缺少必填参数: {', '.join(missing_params)}, 请求来源: {run_request.source_system}, 设备: {run_request.source_device}")
                return error_response(
                    message=f"缺少必填参数: {', '.join(missing_params)}", 
                    code=400
                )
        
        # 检查非系统调度请求的设备是否有正在运行的相同任务
        from data.enum.task_record_enum import SourceType
        
        if run_request.source_type != SourceType.SYSTEM_SCHEDULING:  # 不是系统调度
            # 调用服务层方法检查是否有正在运行的相同任务
            check_result = await TaskEditService.check_running_task_for_device(
                run_request.taskId, 
                run_request.source_device
            )
            
            # 如果有运行中的任务
            if check_result.get("has_running_task", False):
                if check_result.get("allow_restart", False):
                    # 允许重启但仍需记录警告，这是一个需要注意的情况
                    logger.warning(f"设备 {run_request.source_device} 已有相同任务正在运行，但允许重新启动，来源: {run_request.source_system}")
                else:
                    # 不允许重启，记录警告并返回错误
                    logger.warning(f"设备 {run_request.source_device} 已有相同任务正在运行且不允许重启，请求被拒绝，来源: {run_request.source_system}")
                    return error_response(
                        message=check_result.get("message", "相同设备已有此任务正在运行中，请等待任务完成后再次启动"),
                        code=409  # 冲突状态码
                    )
        
        # 执行任务，传入客户端IP和设备信息以及系统任务令牌
        result = await TaskEditService.run_task(
            run_request, 
            client_ip=client_ip, 
            client_info=client_info_str,
            tf_api_token=tf_api_token
        )
        if result is None:
            return error_response(message="任务启动失败", code=500)
        if not result.get("success", False):
            return error_response(message=result.get("message", "任务启动失败"), code=500)
        return format_response(data=result, message="任务启动成功")
    except Exception as e:
        logger.error(f"运行任务失败: {str(e)}")
        return error_response(message=f"运行任务失败: {str(e)}", code=500)

@router.post("/stop/{task_record_id}")
async def stop_task(task_record_id: str = Path(..., description="任务记录ID")):
    """
    终止正在运行的任务
    
    Args:
        task_record_id: 任务记录ID，由run_task接口返回的taskRecordId，而非任务定义ID
        
    Returns:
        成功或失败的响应
    """
    try:
        logger.info(f"准备终止任务: {task_record_id}")
        
        # 导入任务调度器
        from services.enhanced_scheduler import scheduler
        
        # 调用调度器取消任务的方法
        result = await scheduler.cancel_task(task_record_id)
        
        if not result.get("success", False):
            logger.warning(f"终止任务失败: {result.get('message')}")
            return error_response(
                message=result.get("message", "终止任务失败"), 
                code=400
            )
        
        logger.info(f"任务已成功终止: {task_record_id}")
        return format_response(
            data={
                "taskRecordId": task_record_id,
                "status": "canceled"
            },
            message="任务已成功终止"
        )
    except Exception as e:
        logger.log_error_with_trace(f"终止任务异常", e)
        return error_response(message=f"终止任务失败: {str(e)}", code=500)


@router.post("/input-params/{id}")
async def save_input_params(
    id: str = Path(..., description="任务ID"),
    input_params: List[TaskInputParam] = Body(..., embed=True)
):
    """
    保存指定任务的输入参数配置信息
    系统支持根据获取可用输入参数类型接口返回的字段动态配置参数
    """
    try:
        # 将Pydantic模型转换为字典
        params_dict = [param.model_dump() for param in input_params]
        result = await TaskEditService.save_input_params(id, params_dict)
        
        if not result["success"]:
            return error_response(message=result["message"], code=400)
        
        # 如果数据没有变化，返回特定提示
        if not result["changed"]:
            logger.warning(f"保存输入参数时数据未发生变化: {id}")
            return format_response(message=result["message"])
        
        # 数据有变化且保存成功
        return format_response(
            data={
                "version": result.get("version"),
                "updateTime": result.get("updateTime")
            },
            message=result["message"]
        )
    except Exception as e:
        logger.error(f"保存输入参数失败: {str(e)}")
        return error_response(message=f"保存输入参数失败: {str(e)}", code=500)

@router.get("/input-params/{id}")
async def get_input_param_types(
    id: str = Path(..., description="任务ID")
):
    """
    获取指定任务的输入参数配置信息
    用于编辑任务时展示当前任务已配置的输入参数
    """
    try:
        result = await TaskEditService.get_task_input_params(id)
        return result
    except Exception as e:
        return error_response(message=f"获取任务输入参数配置失败: {str(e)}", code=500)

@router.post("/basic-settings/{id}")
async def save_basic_settings(
    id: str = Path(..., description="任务ID"),
    basic_info: TaskBasicInfo = Body(...)
):
    """
    保存任务的基本信息设置
    包括任务名称、备注、是否在任务异常结束或终止时解锁库位等配置
    """
    try:
        result = await TaskEditService.save_basic_settings(id, basic_info)
        
        if not result["success"]:
            return error_response(message=result["message"], code=404)
        
        # 如果数据没有变化，返回特定提示，但仍然返回当前数据
        if not result["changed"]:
            return format_response(
                data={
                    "id": result["id"],
                    "label": result["label"],
                    "remark": result["remark"],
                    "releaseSites": result["releaseSites"],
                    "mapId": result["mapId"]
                },
                message=result["message"]
            )
        
        # 数据有变化且保存成功
        return format_response(
            data={
                "id": result["id"],
                "label": result["label"],
                "remark": result["remark"],
                "releaseSites": result["releaseSites"],
                "mapId": result["mapId"],
                "updateTime": result["updateTime"]
            },
            message=result["message"]
        )
    except Exception as e:
        return error_response(message=f"保存基本设置失败: {str(e)}", code=500)


@router.get("/basic-settings/{id}")
async def get_basic_settings(
    id: str = Path(..., description="任务ID")
):
    """
    获取任务的基本信息设置
    包括任务名称、备注、是否在任务异常结束或终止时解锁库位等配置
    """
    try:
        result = await TaskEditService.get_basic_settings(id)
        
        if not result["success"]:
            return error_response(message=result["message"], code=404)
        
        return format_response(data=result["data"])
    except Exception as e:
        return error_response(message=f"获取任务基本设置失败: {str(e)}", code=500)


@router.get("/common-params")
async def get_common_params():
    """
    获取常用参数的展示字段和字段值类型
    用于任务编辑页面中间部分上侧的常用字段展示区域
    """
    try:
        result = await TaskEditService.get_common_params()
        return format_response(data=result)
    except Exception as e:
        return error_response(message=f"获取常用参数失败: {str(e)}", code=500)

