package com.fs.company.service.impl.call.node;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fs.common.utils.StringUtils;
import com.fs.common.utils.spring.SpringUtils;
import com.fs.company.domain.*;
import com.fs.company.mapper.CompanyVoiceRoboticCallLogCallphoneMapper;
import com.fs.company.mapper.CompanyVoiceRoboticCalleesMapper;
import com.fs.company.mapper.CompanyWorkflowNodeMapper;
import com.fs.company.param.ExecutionContext;
import com.fs.company.service.ICompanyVoiceRoboticService;
import com.fs.company.service.easycall.IEasyCallService;
import com.fs.company.service.impl.CompanyVoiceRoboticCallLogCallphoneServiceImpl;
import com.fs.company.vo.AiCallConfigVO;
import com.fs.company.vo.AiCallWorkflowConditionVo;
import com.fs.company.vo.ExecutionResult;
import com.fs.company.vo.easycall.EasyCallCommonAddCallListParam;
import com.fs.company.vo.easycall.EasyCallCreateTaskParam;
import com.fs.company.vo.easycall.EasyCallPhoneItemVO;
import com.fs.company.vo.easycall.EasyCallTaskVO;
import com.fs.enums.ExecutionStatusEnum;
import com.fs.enums.NodeTypeEnum;
import com.fs.his.config.CidPhoneConfig;
import com.fs.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author MixLiu
 * @date 2026/1/28 13:39
 * @description AI外呼电话任务节点
 */
@Slf4j
public class AiCallTaskNode extends AbstractWorkflowNode {
    private static final CompanyWorkflowNodeMapper companyWorkflowNodeMapper = SpringUtils.getBean(CompanyWorkflowNodeMapper.class);
    private static final ICompanyVoiceRoboticService companyVoiceRoboticService = SpringUtils.getBean(ICompanyVoiceRoboticService.class);
    /** EasyCallCenter365 外呼服务 */
    private static final IEasyCallService easyCallService = SpringUtils.getBean(IEasyCallService.class);
    /** 被叫人表 Mapper，用于获取手机号 */
    private static final CompanyVoiceRoboticCalleesMapper companyVoiceRoboticCalleesMapper = SpringUtils.getBean(CompanyVoiceRoboticCalleesMapper.class);
    private static final CompanyVoiceRoboticCallLogCallphoneServiceImpl companyVoiceRoboticCallLogCallphoneService = SpringUtils.getBean(CompanyVoiceRoboticCallLogCallphoneServiceImpl.class);
    /** EasyCall 外呼回调信息存放到 Redis 的 key 前缀 */
    public static final String EASYCALL_WORKFLOW_REDIS_KEY = "easycall:workflow:callback:";
    public static final String DELAY_CALL_KEY = "aiCallTask:delay:%s:%s:%s:";
    private final String CALL_FROM_CALLBACK = "callBack";
    private final String CALL_FROM_TIMER = "timer";

    private final ISysConfigService configService = SpringUtils.getBean(ISysConfigService.class);

    private final CompanyVoiceRoboticCallLogCallphoneMapper companyVoiceRoboticCallLogCallphoneMapper = SpringUtils.getBean(CompanyVoiceRoboticCallLogCallphoneMapper.class);;

    public AiCallTaskNode(String nodeKey, String nodeName, Map<String, Object> properties) {
        super(nodeKey, nodeName, properties);
    }

    /**
     * 收到回调以后，继续判定和执行下一步动作
     *
     * @param context
     * @return
     */
    @Override
    protected ExecutionResult doContinue(ExecutionContext context) {
        //TODO 多次回调 是否过滤处理
        CompanyAiWorkflowExec exec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(context.getWorkflowInstanceId());
        if (exec == null) {
            log.error("doContinue: 工作流执行实例不存在 - workflowInstanceId: {}", context.getWorkflowInstanceId());
            return null;
        }
        List<CompanyWorkflowEdge> edges = companyWorkflowEdgeMapper.selectListByWorkflowIdAndNodeKey(exec.getWorkflowId(), nodeKey);
        if (edges == null) {
            return null;
        }
        //获取外呼回调结果日志
        String callBackUuid = context.getVariable("callBackUuid", String.class);
        if (StringUtils.isBlank(callBackUuid)) {
            log.error("doContinue: 缺少 callBackUuid - workflowInstanceId: {}", context.getWorkflowInstanceId());
            super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
            return null;
        }
        CompanyVoiceRoboticCallLogCallphone callRes = super.companyVoiceRoboticCallLogCallphoneMapper.selectCallLogByCallbackUuid(callBackUuid);
        if (callRes == null) {
            log.error("doContinue: 未找到外呼回调日志 - callBackUuid: {}, workflowInstanceId: {}", callBackUuid, context.getWorkflowInstanceId());
            super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
            return null;
        }
        //记录执行路线
        int runnableCount = 0;
        for (CompanyWorkflowEdge edge : edges) {
            List<AiCallWorkflowConditionVo> conditions = StringUtils.isBlank(edge.getConditionExpr())
                    ? null
                    : JSONObject.parseArray(edge.getConditionExpr(), AiCallWorkflowConditionVo.class);
//            Boolean isValid = true;
            if (null == conditions || conditions.isEmpty()) {
                runNextNode(context, edge);
                runnableCount++;
                break;
            } else {
                //暂时考虑单条件
                AiCallWorkflowConditionVo condition = conditions.get(0);
                //拨通
                if (condition.isCallConnected() && callRes.getCallTime() != null && callRes.getCallTime() > 0) {
                    //如果含有意向度过滤
                    if (StringUtils.isNotBlank(condition.getIntention())) {
                        if (condition.getIntention().equals(callRes.getIntention())) {
                            runNextNode(context, edge);
                            runnableCount++;
                            break;
                        } else {
                            //不符合意向度配置的回调不执行逻辑
                            log.info("流程：{}，节点:{}，意向度设置：{}，实际意向度：{}， 意向度不符设置中断执行，", context.getWorkflowInstanceId(), nodeKey, condition.getIntention(), callRes.getIntention());

                        }
                    } else {
                        runNextNode(context, edge);
                        runnableCount++;
                        break;
                    }
                }
                //未拨通
                else if (!condition.isCallConnected() && (callRes.getCallTime() == null || Long.valueOf(0).equals(callRes.getCallTime()) || callRes.getCallAnswerTime() == null)) {
                    //延时操作
                    if (null != condition.getCallTime() && condition.getCallTime() > 0) {
                        //计算延时分片分钟
                        long l = System.currentTimeMillis() + condition.getCallTime() * 60 * 1000;
                        ExecutionContext nextContext = context.clone();
                        nextContext.setCurrentNodeKey(edge.getTargetNodeKey());
                        //添加到延时扫描redis
                        super.redisCache.setCacheObject(this.getDelayCallKeyPrefix(exec.getCidGroupNo(),l) + exec.getWorkflowInstanceId(), nextContext, 1, TimeUnit.DAYS);
                        super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), context, ExecutionStatusEnum.WAITING);
                        updateLogStatusIfExist(context, ExecutionStatusEnum.PAUSED, ExecutionStatusEnum.WAITING);
                        runnableCount++;
                        break;
                    }
                    //无时间驱动
                    else {
                        runNextNode(context, edge);
                        runnableCount++;
                        break;
                    }
                }
            }

        }
        //如果没有节点可执行通路 条件均不满足 则标记流程为中断
        if(runnableCount < 1){
            super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
        }

        return null;
    }

    @Override
    protected ExecutionResult doExecute(ExecutionContext context) {
        try {
            if (isAsync()) {
                CompanyWorkflowNode node = context.getVariable("currentNode", CompanyWorkflowNode.class);
                if (node == null) {
                    node = getNodeByKey(nodeKey);
                }
                String nodeConfig = node.getNodeConfig();
                if (StringUtils.isBlank(nodeConfig)) {
                    log.error("流程：{} 节点:{} 未配置节点配置", context.getWorkflowInstanceId(), nodeKey);
                    super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
                    return ExecutionResult.failure().errorMessage("未配置节点配置").build();
                }
                AiCallConfigVO callConfigVo = JSONObject.parseObject(nodeConfig, AiCallConfigVO.class);
                if (callConfigVo == null) {
                    super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
                    return ExecutionResult.failure().errorMessage("节点配置解析失败").build();
                }

                //执行外呼逻辑 需要传入节点信息
                CompanyVoiceRoboticBusiness bus = super.getRoboticBusiness(context.getWorkflowInstanceId());
                if (bus == null) {
                    return ExecutionResult.failure().errorMessage("未找到业务数据").build();
                }

                //进入手机号拨打次数校验
                if(checkPhoneCallLimit(bus.getId())){
                    super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
                    return ExecutionResult.failure().errorMessage("今日拨打次数已达上限！").build();
                }

                //手动外呼配置 1、人工 2、ai外呼
                if(Integer.valueOf(1).equals(callConfigVo.getCallMode())){
                    super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), context, ExecutionStatusEnum.WAITING_DO_CALL);
                    return ExecutionResult.paused()
                            .outputData(context.getVariables())
                            .nextNodeKey("").build();
                }
//                companyVoiceRoboticService.workflowCallPhoneOne(bus.getRoboticId(), bus.getCalleeId(), context, callConfigVo);
                // EasyCallCenter365 外呼
                 workflowCallPhoneOne4EasyCall(bus.getRoboticId(),bus.getCalleeId(), context, callConfigVo);
                super.asyncWorkflowForBlockingNode(context.getWorkflowInstanceId(), context.getCurrentNodeKey(), context, ExecutionStatusEnum.PAUSED);
                return ExecutionResult.paused()
                        .outputData(context.getVariables())
                        .nextNodeKey("").build();
            } else {
                return ExecutionResult.failure()
                        .nextNodeKey(null).build();
            }
        } catch (Exception ex) {
            log.error("流程：{}：节点:{}执行失败，", context.getWorkflowInstanceId(), nodeKey, ex);
            super.updateWorkflowStatus(context.getWorkflowInstanceId(), ExecutionStatusEnum.INTERRUPT);
            return ExecutionResult.failure()
                    .nextNodeKey(null).build();
        }
    }

    @Override
    public NodeTypeEnum getType() {
        return NodeTypeEnum.AI_CALL_TASK;
    }

    @Override
    public Boolean isAsync() {
        return true;
    }

    /**
     * getRedisCacheKey
     * @param cidGroupNo
     * @param time
     * @return
     */
    public static String getDelayCallKeyPrefix(Integer cidGroupNo,Long time) {
        Date nowDay = new Date();
        if (null != time) {
            nowDay = new Date(time);
        }
        return String.format(DELAY_CALL_KEY, cidGroupNo,nowDay.getHours(), nowDay.getMinutes());
    }

    /**
     * EasyCallCenter365 外呼节点核心方法（替代旧天天外呼的 workflowCallPhoneOne）
     * <p>
     * 执行流程：
     * 1. 生成 callBackUuid 存入 Redis（用于后续回调匹配）
     * 2. 查询被叫人手机号
     * 3. 调用 EasyCallCenter365 创建 AI 外呼任务
     * 4. 将被叫号码加入任务名单
     * 5. 启动外呼任务
     *
     * @param calleeId    被叫人记录 ID（对应 company_voice_robotic_callees 表）
     * @param context     工作流执行上下文，含 workflowInstanceId、currentNodeKey 等
     * @param callConfigVo 节点配置，包含外呼线路 ID、大模型底座 ID、音色、技能组等参数
     */
    private void workflowCallPhoneOne4EasyCall(Long roboticId,Long calleeId, ExecutionContext context, AiCallConfigVO callConfigVo) {

        CompanyVoiceRobotic robotic = companyVoiceRoboticMapper.selectById(roboticId);
        // 1. 生成回调唯一标识符，后续回调时通过此 uuid 匹配对应的流程实例
        String callBackUuid = UUID.randomUUID().toString();
        // 将回调信息写入 Redis，保存 1 天
        JSONObject callbackInfo = new JSONObject();
        callbackInfo.put("callBackUuid", callBackUuid);
        callbackInfo.put("nodeKey", context.getCurrentNodeKey());
        callbackInfo.put("workflowInstanceId", context.getWorkflowInstanceId());
        callbackInfo.put("calleeId", calleeId);
        super.redisCache.setCacheObject(EASYCALL_WORKFLOW_REDIS_KEY + callBackUuid,
                callbackInfo.toJSONString(), 1, TimeUnit.DAYS);
        // 将 callBackUuid 写入 context，供后续回调时从 context 取用
        context.setVariable("callBackUuid", callBackUuid);

        // 2. 获取被叫人手机号
        CompanyVoiceRoboticCallees callees = companyVoiceRoboticCalleesMapper.selectById(calleeId);
        if (callees == null || StringUtils.isBlank(callees.getPhone())) {
            log.error("workflowCallPhoneOne4EasyCall: 被叫人不存在或手机号为空 - calleeId: {}", calleeId);
            throw new RuntimeException("被叫人信息异常，calleeId: " + calleeId);
        }

        // 3. 构建创建任务参数（AI 外呼模式：taskType=1）
        EasyCallCreateTaskParam createParam = new EasyCallCreateTaskParam();
        // 任务名称：使用工作流实例 ID + 被叫人 ID 组合，保证唯一性
        createParam.setBatchName("workflow_" + context.getWorkflowInstanceId() + "_" + calleeId);
        createParam.setThreadNum(100L);
        // AI 外呼模式
        createParam.setTaskType(1);
        // 外呼线路（网关）
        createParam.setGatewayId(callConfigVo.getGatewayId());
        // 大模型底座
        createParam.setLlmAccountId(callConfigVo.getLlmAccountId());
        // 音色编号
        createParam.setVoiceCode(callConfigVo.getVoiceCode());
        // 音色来源（如未配置默认留空，由 EasyCallCenter365 使用默认值）
        createParam.setVoiceSource(callConfigVo.getVoiceSource());
        // 技能组（转人工客服分组，可选）
//        createParam.setGroupId(callConfigVo.getBusiGroupId());

        JSONObject runParam = (JSONObject) JSON.toJSON(createParam);
        runParam.put("companyId", robotic.getCompanyId());
        CompanyVoiceRoboticCallLogCallphone addLog = CompanyVoiceRoboticCallLogCallphone.initCallLog(
                runParam.toJSONString(), calleeId, roboticId, robotic.getCompanyId());
        // 4. 调用 EasyCallCenter365 创建任务接口
        // companyId 传 null 是因为 EasyCallCenter365 是全局地址，不需要按公司隔离
        log.info("workflowCallPhoneOne4EasyCall: 创建 EasyCall 任务 - workflowInstanceId: {}, calleeId: {}",
                context.getWorkflowInstanceId(), calleeId);
        EasyCallTaskVO task = easyCallService.createTask(createParam, null);
        if (task == null || task.getBatchId() == null) {
            log.error("workflowCallPhoneOne4EasyCall: 创建 EasyCall 任务失败 - workflowInstanceId: {}",
                    context.getWorkflowInstanceId());
            throw new RuntimeException("EasyCallCenter365 创建任务失败");
        }
        Long batchId = task.getBatchId();
        log.info("workflowCallPhoneOne4EasyCall: EasyCall 任务创建成功 - batchId: {}", batchId);

        // 5. 将被叫号码加入任务名单（使用通用追加接口，支持传入业务数据）
        EasyCallCommonAddCallListParam addListParam = new EasyCallCommonAddCallListParam();
        addListParam.setBatchId(batchId);
        // 构建号码条目，bizJson 传入默认客户信息占位符
        EasyCallPhoneItemVO phoneItem = new EasyCallPhoneItemVO();
        phoneItem.setPhoneNum(callees.getPhone());
        // bizJson 默认传入客户姓名占位，运行时可根据实际业务填充
        phoneItem.setBizJson(new JSONObject().fluentPut("custName", callees.getUserName()).fluentPut("callBackUuid",callBackUuid));
        addListParam.setPhoneList(Collections.singletonList(phoneItem));
        easyCallService.addCommonCallList(addListParam, null);
        log.info("workflowCallPhoneOne4EasyCall: 名单追加成功 - batchId: {}, phone: {}",
                batchId, callees.getPhone());

        // 6. 启动外呼任务
        easyCallService.startTask(batchId, null);
        log.info("workflowCallPhoneOne4EasyCall: 任务启动成功 - batchId: {}", batchId);
        addLog.setStatus(1);
        addLog.setCallbackUuid(callBackUuid);
        companyVoiceRoboticCallLogCallphoneService.asyncInsertCompanyVoiceRoboticCallLog(addLog);
        // 7. 将 batchId 写入 context，方便后续节点使用
        context.setVariable("easyCallBatchId", batchId);
    }

    private boolean checkPhoneCallLimit(Long businessId){
        String json = configService.selectConfigByKey("cid.config");
        if(StringUtils.isNotEmpty(json)){//数据存在
            //转换数据
            CidPhoneConfig config =JSONObject.parseObject(json,CidPhoneConfig.class);
            if(config.getEnablePhoneLimitConfig() != null && config.getEnablePhoneLimitConfig()){//开启了拨打限制按钮
                //获取销售公司
                Long companyId = companyVoiceRoboticCallLogCallphoneMapper.selectCompanyIdByBusinessId(businessId);
                if(companyId != null){
                    //获取当前外呼业务电话拨打信息
                    int num = companyVoiceRoboticCallLogCallphoneMapper.countTodayCallsByBusinessId(businessId,companyId);
                    if(num >= config.getNumberCalls()){
                        return true;
                    }
                }
            }
        }
        return false;
    }

//    @Override
//    protected void postExecute(ExecutionContext context, ExecutionResult result) {
//        super.postExecute(context, result);
//        String callRedisKey = context.getVariable("callRedisKey", String.class);
//        //来源于定时调用doexec，调用后移除key
//        if (StringUtils.isNotBlank(callRedisKey)) {
//            super.redisCache.deleteObject(callRedisKey);
//        }
//
//    }


//    @Override
//    public String getNextNodeKey(String workflowInstanceId, String nodeKey) {
//
//        CompanyAiWorkflowExec companyAiWorkflowExec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(workflowInstanceId);
//
//        List<CompanyWorkflowEdge> companyWorkflowEdges =
//                companyWorkflowEdgeMapper.selectListByWorkflowIdAndNodeKey(companyAiWorkflowExec.getWorkflowId(), nodeKey);
//        //多线路
//        if (null != companyWorkflowEdges && !companyWorkflowEdges.isEmpty()) {
//            if (companyWorkflowEdges.size() > 1) {
//                for (CompanyWorkflowEdge edge : companyWorkflowEdges) {
//                    AiCallWorkflowConditionVo aiCallWorkflowConditionVo = JSON.parseObject(edge.getConditionExpr(), AiCallWorkflowConditionVo.class);
//                    //todo
//
//                }
//            } else {
//                return companyWorkflowEdges.get(0).getTargetNodeKey();
//            }
//        }
//        //没有满足的节点
//        return null;
//    }

}
