package com.backendsys.service.SDKService.SDKTencent;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.backendsys.aspect.HttpRequestAspect;
import com.backendsys.entity.Ai.Aiivh.*;
import com.backendsys.entity.Ai.Aiivh.AiivhAsset.AiivhAssetQuotaStatisticsDTO;
import com.backendsys.entity.Ai.Aiivh.AiivhMakeBroadcastTask.SpeechParamDTO;
import com.backendsys.entity.Ai.Aiivh.AiivhMakeBroadcastTask.VideoParamAnchorParamDTO;
import com.backendsys.entity.Ai.Aiivh.AiivhMakeBroadcastTask.VideoParamDTO;
import com.backendsys.entity.Ai.Aiivh.AiivhMakeBroadcastTask.VideoParamLogoParamDTO;
import com.backendsys.entity.Tencent.*;
import com.backendsys.enums.IvhQuotaActiveEvent;
import com.backendsys.exception.CustException;
import com.backendsys.mapper.Ai.Aiivh.AiivhQuotaHistoryMapper;
import com.backendsys.mapper.Ai.Aiivh.AiivhQuotaMapper;
import com.backendsys.service.Ai.Aiivh.AiivhAssetService;
import com.backendsys.service.Ai.Aiivh.AiivhMakeVirtualmanTaskService;
import com.backendsys.utils.ListUtil;
import com.backendsys.utils.MapUtil;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import java.lang.reflect.Field;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class SDKTencentAiVirtualmanServiceImpl implements SDKTencentAiVirtualmanService {

    @Lazy
    @Autowired
    RedissonClient redissonClient;

    @Autowired
    private HttpRequestAspect httpRequestAspect;
    @Autowired
    private SDKTencentService sdkTencentService;
    @Autowired
    private AiivhMakeVirtualmanTaskService aiivhMakeVirtualmanTaskService;
    @Autowired
    private AiivhAssetService aiivhAssetService;

    @Autowired
    private AiivhQuotaMapper aiivhQuotaMapper;
    @Autowired
    private AiivhQuotaHistoryMapper aiivhQuotaHistoryMapper;


    @Value("${tencent.ivh.app-key}")
    private String appKey;
    @Value("${tencent.ivh.access-token}")
    private String accessToken;
    @Value("${tencent.ivh.empty-app-key}")
    private String emptyAppKey;
    @Value("${tencent.ivh.empty-access-token}")
    private String emptyAccessToken;

    /**
     * [腾讯-数智人] 初始化 定制数智人列表 (使用公司账号)
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> initCustomVirtualman(Integer pageIndex, Integer pageSize) {
        Map<String, Object> payload = new HashMap<>();
        payload.put("VirtualmanTypeCode", "small_sample_2d");
        // payload.put("AnchorCodes", "Array of [string]");
        payload.put("PageIndex", pageIndex);
        payload.put("PageSize", pageSize);
        Map<String, Object> resultMap = Collections.singletonMap("Payload", payload);
        String bodyData = JSONUtil.toJsonStr(resultMap);

        // -- Http Request --------------------------------------------
        /**
         * 这里使用我自己的空白密钥，因为只有未创建自定义形象的账号，才能返回全部形象
         */
        Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken); // 获得 Signature 签名

        String url = "https://gw.tvs.qq.com/v2/ivh/crmserver/customerassetservice/describesmallsampleimage";
        URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                .queryParam("appkey", signature.get("appkey"))
                .queryParam("signature", "{signature}")
                .queryParam("timestamp", signature.get("timestamp"))
                .build(signature.get("signature"));

        // Http 远程调用
        Map<String, Object> resp = WebClient.create()
                .method(HttpMethod.POST)
                .uri(uriWithParams)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .bodyValue(bodyData.toString())
                .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
                })
                .block();

        return resp;
    }

    /**
     * [腾讯-数智人] 初始化公共数智人列表 (使用空白账号)
     * 注：有效期：2024/03/25-2024/04/08 过期后能否继续查询？
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> initPublicVirtualman(Integer pageIndex, Integer pageSize) {

        Map<String, Object> payload = new HashMap<>();
        payload.put("VirtualmanTypeCode", "small_sample_2d");
        // payload.put("AnchorCodes", "Array of [string]");
        payload.put("PageIndex", pageIndex);
        payload.put("PageSize", pageSize);
        Map<String, Object> resultMap = Collections.singletonMap("Payload", payload);
        String bodyData = JSONUtil.toJsonStr(resultMap);

        // -- Http Request --------------------------------------------
        /**
         * 这里使用我自己的空白密钥，因为只有未创建自定义形象的账号，才能返回全部形象
         */
        Map<String, Object> signature = sdkTencentService.getSignature(emptyAppKey, emptyAccessToken); // 获得 Signature 签名

        String url = "https://gw.tvs.qq.com/v2/ivh/crmserver/customerassetservice/describesmallsampleimage";
        URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                .queryParam("appkey", signature.get("appkey"))
                .queryParam("signature", "{signature}")
                .queryParam("timestamp", signature.get("timestamp"))
                .build(signature.get("signature"));

        // Http 远程调用
        Map<String, Object> resp = WebClient.create()
                .method(HttpMethod.POST)
                .uri(uriWithParams)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .bodyValue(bodyData.toString())
                .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
                })
                .block();

        return resp;
    }

    
    
    /**
     * 基于实体类，将 anchor_name 转换为 AnchorName (将下划线转为大驼峰)
     *
     * Map<String, Object> resultMap = new HashMap<>();
     * convertObjectToMap(tencentCustomserviceDTO, resultMap);
     * System.out.println(resultMap);
     */
    public static void convertObjectToMap(Object obj, Map<String, Object> resultMap) throws IllegalAccessException {
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(obj);
            String fieldName = field.getName();
            String convertedFieldName = StrUtil.upperFirst(StrUtil.toCamelCase(fieldName));

            if (value != null) {
                /**
                 * 注意：需要手动填写实体类
                 */
                if (value instanceof List) {
                    // 如果值是列表类型，则遍历列表中的每个对象
                    List<Object> list = (List<Object>)value;
                    Map<String, Object> nestedMap = new HashMap<>();
                    List<Map<String, Object>> nestedList = new ArrayList<>();
                    for (Object item : list) {
                        Map<String, Object> itemMap = new HashMap<>();
                        convertObjectToMap(item, itemMap);
                        nestedList.add(itemMap);
                    }
                    resultMap.put(convertedFieldName, nestedList);
                } else if (value.getClass().isAssignableFrom(VideoParamAnchorParamDTO.class) ||
                        value.getClass().isAssignableFrom(VideoParamLogoParamDTO.class) ||
                        value.getClass().isAssignableFrom(VideoParamDTO.class) ||
                        value.getClass().isAssignableFrom(SpeechParamDTO.class) ||
                        value.getClass().isAssignableFrom(TencentCustomservicePayloadDTO.class) ||
                        value.getClass().isAssignableFrom(TencentCustomserviceConfirmPlayloadDTO.class) ||
                        value.getClass().isAssignableFrom(TencentCustomserviceHeaderDTO.class) ||
                        value.getClass().isAssignableFrom(Payload.class)) {
                    // If the value is a nested object, recursively convert it to map
                    Map<String, Object> nestedMap = new HashMap<>();
                    convertObjectToMap(value, nestedMap);
                    //
                    fieldName = field.getName();
                    convertedFieldName = StrUtil.upperFirst(StrUtil.toCamelCase(fieldName));
                    resultMap.put(convertedFieldName, nestedMap);
                } else {
                    fieldName = field.getName();
                    convertedFieldName = StrUtil.upperFirst(StrUtil.toCamelCase(fieldName));
                    resultMap.put(convertedFieldName, value);
                }
            }
        }
    }

    private String getAnchorNameView(String anchor_name) {
        LocalDateTime now = LocalDateTime.now();
        String timestamp = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        return anchor_name + "_" + timestamp;
    }

    /**
     * [腾讯-数智人] 创建形象/音色定制任务
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> makeVirtualTask(TencentCustomserviceDTO tencentCustomserviceDTO) {
        RLock lock = redissonClient.getLock("makeVirtualTask");
        try { lock.tryLock(3, TimeUnit.SECONDS);

            // 参数校验：
            // - IdentityWrittenCosUrl 二选一或二者都传
            // - IdentityCosUrl 二选一或二者都传

            // 基于实体类，将 anchor_name 转换为 AnchorName (将下划线转为大驼峰)
            Map<String, Object> resultMap = new HashMap<>();

            // 生成: AnchorName 数智人名称_时间戳 (使接口值唯一，本地数据不唯一)
            // - anchor_name 唯一
            // - anchor_name_view 不唯一
            TencentCustomservicePayloadDTO payload = tencentCustomserviceDTO.getPayload();
            payload.setAnchor_name_view(getAnchorNameView(payload.getAnchor_name()));
            tencentCustomserviceDTO.setPayload(payload);

            convertObjectToMap(tencentCustomserviceDTO, resultMap);
            String bodyData = JSONUtil.toJsonStr(resultMap);

            //        System.out.println("resultMap:");
            //        System.out.println(resultMap);
            //        System.out.println("-----------------------------------------");
            //        System.out.println("bodyData:");
            //        System.out.println(bodyData);
            //        System.out.println("-----------------------------------------");

            // -- Http Request --------------------------------------------
            Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken); // 获得 Signature 签名

            String url = "https://gw.tvs.qq.com/v2/ivh/assetmanager/customservice/make";
            URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                    .queryParam("appkey", signature.get("appkey"))
                    .queryParam("signature", "{signature}")
                    .queryParam("timestamp", signature.get("timestamp"))
                    .build(signature.get("signature"));

            // Http 远程调用
            Map<String, Object> resp = WebClient.create()
                    .method(HttpMethod.POST)
                    .uri(uriWithParams)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .bodyValue(bodyData)
                    .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
                    })
                    .block();

            System.out.println(resp);

            //// 临时数据 (模拟成功)
            //Map<String, Object> tempPayload = new LinkedHashMap<>();
            //String uuid = String.valueOf(UUID.randomUUID());
            //tempPayload.put("TaskId", uuid);
            //resp.put("Payload", tempPayload);

            /**
             * 创建任务 (远程)
             */

            // 判断 任务是否创建成功 (是，则创建数据库记录)
            Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
            if (respHeaderCode == 0) {

                /**
                 * 创建任务 (本地)
                 */
                TencentCustomservicePayloadDTO tencentCustomservicePayloadDTO = tencentCustomserviceDTO.getPayload();
                String make_type = tencentCustomservicePayloadDTO.getMake_type();

                // -- [Post] 创建数智人任务 --------------------------------------------------------
                AiPersonDTO aiPersonDTO = new AiPersonDTO();
                // [查询] 自身 UserId
                Long user_id = httpRequestAspect.getUserId();
                aiPersonDTO.setUser_id(user_id);
                //
                aiPersonDTO.setRequest_id((String) MapUtil.get(resp, "Header.RequestID"));
                aiPersonDTO.setTask_id((String) MapUtil.get(resp, "Payload.TaskId"));
                aiPersonDTO.setAnchor_name(tencentCustomservicePayloadDTO.getAnchor_name_view());
                aiPersonDTO.setAnchor_name_view(tencentCustomservicePayloadDTO.getAnchor_name());
                aiPersonDTO.setMake_type(make_type);
                aiPersonDTO.setIdentity_cos_url(tencentCustomservicePayloadDTO.getIdentity_cos_url());
                aiPersonDTO.setIdentity_written_cos_url(tencentCustomservicePayloadDTO.getIdentity_written_cos_url());
                aiPersonDTO.setMaterial_cos_url(tencentCustomservicePayloadDTO.getMaterial_cos_url());
                aiPersonDTO.setIs_remove_background(tencentCustomservicePayloadDTO.getIs_remove_background());
                aiPersonDTO.setSex_type(tencentCustomservicePayloadDTO.getSex_type());
                aiPersonDTO.setNotes(tencentCustomservicePayloadDTO.getNotes());
                aiPersonDTO.setText_driver(tencentCustomservicePayloadDTO.getText_driver());
                aiPersonDTO.setVoice_driver_cos_file(tencentCustomservicePayloadDTO.getVoice_driver_cos_file());
                aiivhMakeVirtualmanTaskService.insertAiivhMakeVirtualmanTask(aiPersonDTO);

                /**
                 * 创建任务之后，此时是 { "Stage": EXAMINE } 状态，还未生成任务，
                 * 大概1分钟内才会更变状态
                 *
                 * 创建任务成功后，创建一个临时 [形象] 记录，主要是记录 user_id 和 anchor_name 字段
                 * 等待用户访问查询详情接口之后，再更新 [形象详情]
                 */
                if(make_type.equals("IMAGE_GENERAL")) {
                    // 定制形象 (创建临时记录)
                    AiivhAnchorDTO aiivhAnchorDTO = new AiivhAnchorDTO();
                    aiivhAnchorDTO.setAnchor_name(tencentCustomservicePayloadDTO.getAnchor_name_view());
                    aiivhAnchorDTO.setAnchor_name_view(tencentCustomservicePayloadDTO.getAnchor_name());
                    aiivhAnchorDTO.setUser_id(user_id);
                    aiivhMakeVirtualmanTaskService.insertAiivhAnchorCustom(aiivhAnchorDTO);
                }
                if(make_type.equals("VOICE")) {
                    // 定制音色 (创建临时记录)
                    AiPersonTimbreCustomDTO aiPersonTimbreCustomDTO = new AiPersonTimbreCustomDTO();
                    aiPersonTimbreCustomDTO.setTimbre_name(tencentCustomservicePayloadDTO.getAnchor_name_view());
                    aiPersonTimbreCustomDTO.setTimbre_name_view(tencentCustomservicePayloadDTO.getAnchor_name());
                    aiPersonTimbreCustomDTO.setUser_id(user_id);
                    aiivhMakeVirtualmanTaskService.insertAiPersoneTimbreCustom(aiPersonTimbreCustomDTO);
                }

            }

            return resp;

        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) { throw new RuntimeException(e);
        } finally { lock.unlock(); }

    }


    /**
     * [腾讯-数智人] 创建定制形象任务 (未提交远程)
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> insertVirtualmanTask(TencentCustomserviceDTO tencentCustomserviceDTO) {

        TencentCustomservicePayloadDTO tencentCustomservicePayloadDTO = tencentCustomserviceDTO.getPayload();

        // -- 将带 Payload 的参数进行格式化 --------------------------------------------------------
        AiPersonDTO aiPersonDTO = new AiPersonDTO();
        // [查询] 自身 UserId
        Long user_id = httpRequestAspect.getUserId();
        aiPersonDTO.setUser_id(user_id);
        //
        // aiPersonDTO.setRequest_id((String) MapUtil.get(resp, "Header.RequestID"));
        // aiPersonDTO.setTask_id((String) MapUtil.get(resp, "Payload.TaskId"));

        String anchor_name = tencentCustomservicePayloadDTO.getAnchor_name();
        aiPersonDTO.setAnchor_name(getAnchorNameView(anchor_name));
        aiPersonDTO.setAnchor_name_view(anchor_name);

        aiPersonDTO.setQuota_code(tencentCustomserviceDTO.getQueue_code());

        aiPersonDTO.setMake_type(tencentCustomserviceDTO.getPayload().getMake_type());
        aiPersonDTO.setIdentity_cos_url(tencentCustomservicePayloadDTO.getIdentity_cos_url());
        aiPersonDTO.setIdentity_written_cos_url(tencentCustomservicePayloadDTO.getIdentity_written_cos_url());
        aiPersonDTO.setMaterial_cos_url(tencentCustomservicePayloadDTO.getMaterial_cos_url());
        aiPersonDTO.setIs_remove_background(tencentCustomservicePayloadDTO.getIs_remove_background());
        aiPersonDTO.setSex_type(tencentCustomservicePayloadDTO.getSex_type());
        aiPersonDTO.setNotes(tencentCustomservicePayloadDTO.getNotes());
        aiPersonDTO.setText_driver(tencentCustomservicePayloadDTO.getText_driver());
        aiPersonDTO.setVoice_driver_cos_file(tencentCustomservicePayloadDTO.getVoice_driver_cos_file());

        aiPersonDTO.setTask_status("AUDIT");

        return aiivhMakeVirtualmanTaskService.insertAiivhMakeVirtualmanTask(aiPersonDTO);
    }




    /*

    // 排队机制（定时任务？）
    // 1.先查询排队表，如果为空，则提交，同时返回任务ID；如果前面有人，则排队，返回排队人数
    //
    // 2.数据插入定制数据人列表 (不执行)，同时插入排队表 (包含提交的数据)
    // 4.用定时任务查询未完成记录 (查询成功或失败，则将排队表中的记录去除；同时将下一个任务自动提交)

    // 这一步还不用排队，要等运营人员点提交才有
//            // -- 使用 Redisson 排队 -----------------------------------------------------------------
//            RList<String> rList = redissonClient.getList("makeVirtualTaskQueue");
//            tencentCustomserviceDTO.setQueue_code(CommonUtil.generateUniqueRandomOrderCode("QUE"));
//            String jsonString = JSONUtil.toJsonStr(tencentCustomserviceDTO);
//            // 入队
//            rList.add(jsonString);

     */

    /**
     * [方法] 查询我的配额，并获得有效(类型匹配，状态可用，未过期)，最近临期的那一条配额
     */
    private Map<String, Object> getEffectiveQuota(String quotaType, String makeType) {
        // [Get] 查询我的配额
        AiivhQuotaDTO aiivhQuotaDTO = new AiivhQuotaDTO();
        aiivhQuotaDTO.setUser_id(httpRequestAspect.getUserId());
        List<Map<String, Object>> myQuota = aiivhQuotaMapper.queryAiivhQuotaList(aiivhQuotaDTO);

        LocalDateTime now = LocalDateTime.now();
        String finalQuotaType = quotaType;
        myQuota = myQuota.stream().filter(map -> {
            String qType = (String) map.get("quota_type");                  // 配额类型 (Anchor|Timbre)
            Integer qStatus = (Integer) map.get("quota_status");            // 配额状态 (1未使用|2使用中|3已使用)
            String expireDateString = (String) map.get("expire_date");      // 过期时间 (对比当前时间)
            LocalDateTime expireDate = LocalDateTime.parse(expireDateString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            return (finalQuotaType.equals(qType)) && qStatus == 1 && !now.isAfter(expireDate);
        }).sorted(Comparator.comparing(map -> {
            String expireDateString = map.get("expire_date").toString();    // 按过期时间排序 (最早创建在最前面)
            return LocalDateTime.parse(expireDateString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        })).collect(Collectors.toList());

        if (myQuota.size() == 0) {
            throw new CustException("(" + makeType + ") 配额不足");
        } else {
            // 获得最近临期的那一条配额
            Map<String, Object> targetQuota = myQuota.get(0);
            return targetQuota;
        }
    }

    /**
     * 在上面定制的基础上增加排队机制
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> makeVirtualTaskQueue(TencentCustomserviceDTO tencentCustomserviceDTO) {

        RLock lock = redissonClient.getLock("makeVirtualTask");
        try { lock.tryLock(3, TimeUnit.SECONDS);

            // [Get] 查询我的配额数量一览
            AiivhAssetQuotaStatisticsDTO aiivhAssetQuotaStatisticsDTO = aiivhAssetService.getMyOverview();

            // 判断配额是否足够
            Long quota_balance = null;
            String makeType = tencentCustomserviceDTO.getPayload().getMake_type();
            String quotaType = null;
            String activeType = null, activeTypeDescription = null;
            // [定制视频] 2D小样本-标准版(通用口型)形象定制
            if (makeType.equals("IMAGE_GENERAL")) {
                quota_balance = aiivhAssetQuotaStatisticsDTO.getAnchor_free_quota();
                quotaType = "Anchor";
                activeType = IvhQuotaActiveEvent.MAKE_ANCHOR.getCode();
                activeTypeDescription = IvhQuotaActiveEvent.MAKE_ANCHOR.getDescription();
            }
            // [定制音色] 2D小样本声音定制
            if (makeType.equals("VOICE")) {
                quota_balance = aiivhAssetQuotaStatisticsDTO.getTimbre_free_quota();
                quotaType = "Timbre";
                activeType = IvhQuotaActiveEvent.MAKE_TIMBRE.getCode();
                activeTypeDescription = IvhQuotaActiveEvent.MAKE_TIMBRE.getDescription();
            }

            if (quota_balance <= 0) {
                throw new CustException("(" + makeType + ") 配额不足");
            } else {

                // -- 1.查询我的配额，并获得有效(类型匹配，状态可用，未过期)，最近临期的那一条配额 -----------------------------
                Map<String, Object> targetQuota = getEffectiveQuota(quotaType, makeType);
                String quotaCode = (String) targetQuota.get("quota_code");
                // -- 2.修改配额状态: 2使用中 ------------------------------------------------------------------------
                AiivhQuotaDTO aiivhQuotaDTO = new AiivhQuotaDTO();
                aiivhQuotaDTO.setQuota_code(quotaCode);
                aiivhQuotaDTO.setQuota_status(2);
                aiivhQuotaMapper.updateAiivhQuota(aiivhQuotaDTO);
                // -- 3.记录 ai_ivh_quota_history 操作 ------------------------------------------------------------
                List<AiivhQuotaHistoryDTO> list = new ArrayList<>();
                AiivhQuotaHistoryDTO aiivhQuotaHistoryDTO = new AiivhQuotaHistoryDTO();
                aiivhQuotaHistoryDTO.setUser_id(httpRequestAspect.getUserId());
                aiivhQuotaHistoryDTO.setActivity_type(activeType);
                aiivhQuotaHistoryDTO.setActivity_type_description(activeTypeDescription);
                aiivhQuotaHistoryDTO.setQuota_type(quotaType);
                aiivhQuotaHistoryDTO.setQuota_adjustment(-1);
                list.add(aiivhQuotaHistoryDTO);
                aiivhQuotaHistoryMapper.insertAiivhQuotaHistoryBatch(list);

                // 创建定制形象任务 (未提交)
                tencentCustomserviceDTO.setQueue_code(quotaCode);
                return insertVirtualmanTask(tencentCustomserviceDTO);

            }

        } catch (InterruptedException e) { throw new RuntimeException(e);
        } finally { lock.unlock(); }
    }


    /**
     * 发起任务 (填写 腾讯订单、备注，同时发起任务)
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> launchVirtualTaskQueue(VirtualmanLaunchDTO virtualmanLaunchDTO) {

        // 1.获得任务详情
        AiPersonDTO aiPersonDTO = new AiPersonDTO();
        aiPersonDTO.setQuota_code(virtualmanLaunchDTO.getQuota_code());

        Map<String, Object> taskDetail = aiivhMakeVirtualmanTaskService.queryAiivhMakeVirtualmanTaskDetail(aiPersonDTO);

        // 2.发起任务
        TencentCustomserviceDTO tencentCustomserviceDTO = new TencentCustomserviceDTO();
        TencentCustomservicePayloadDTO payloadDTO = new TencentCustomservicePayloadDTO();
        payloadDTO.setAnchor_name((String) taskDetail.get("anchor_name"));
        payloadDTO.setMake_type((String) taskDetail.get("make_type"));
        payloadDTO.setIdentity_cos_url((String) taskDetail.get("identity_cos_url"));
        payloadDTO.setIdentity_written_cos_url((String) taskDetail.get("identity_written_cos_url"));
        payloadDTO.setMaterial_cos_url((String) taskDetail.get("material_cos_url"));
        payloadDTO.setIs_remove_background((Integer) taskDetail.get("is_remove_background"));
        payloadDTO.setSex_type((String) taskDetail.get("sex_type"));
        payloadDTO.setNotes((String) taskDetail.get("notes"));
        payloadDTO.setText_driver((String) taskDetail.get("text_driver"));
        payloadDTO.setVoice_driver_cos_file((String) taskDetail.get("voice_driver_cos_file"));
        tencentCustomserviceDTO.setPayload(payloadDTO);

        System.out.println(tencentCustomserviceDTO);
        Map<String, Object> resp = makeVirtualTask(tencentCustomserviceDTO);

        System.out.println("--------------------------");
        System.out.println(resp);

        Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
        if (respHeaderCode != 0) {
            // {Header={RequestID=gz6c6d3da317138514464456410, SessionID=gz6c6d3da317138514464456411, DialogID=, Code=100008, Message=LimitExceeded:超过配额限制: AssetInsufficientError:无可用配额}}
            String message = (String) MapUtil.get(resp, "Header.Message");
            throw new CustException(message);
        } else {
            // 2.更新 任务ID、腾讯订单号、备注
            String task_id = (String) MapUtil.get(resp, "Payload.TaskId");
            aiPersonDTO.setTask_id(task_id);
            aiPersonDTO.setQuota_code(virtualmanLaunchDTO.getQuota_code());
            aiPersonDTO.setTencent_order_code(virtualmanLaunchDTO.getTencent_order_code());
            aiPersonDTO.setLaunch_note(virtualmanLaunchDTO.getLaunch_note());
            return aiivhMakeVirtualmanTaskService.updateAiivhMakeVirtualmanTask(aiPersonDTO);
        }

    }


    /**
     * [腾讯-数智人] 查询形象/音色定制进度
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> getProgressOfVirtualmanTask(String task_id) {
        // -- Http Request --------------------------------------------
        Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken); // 获得 Signature 签名
//        System.out.println("signature: " + signature);

        String url = "https://gw.tvs.qq.com/v2/ivh/assetmanager/customservice/getprogress";
        URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                .queryParam("appkey", signature.get("appkey"))
                .queryParam("signature", "{signature}")
                .queryParam("timestamp", signature.get("timestamp"))
                .build(signature.get("signature"));

        String jsonStr = "{\"Payload\": {\"TaskId\": \"" + task_id + "\"}}";
        JSONObject bodyData = new JSONObject(jsonStr);

//        System.out.println("task_id: " + task_id);
//        System.out.println("bodyData: " + bodyData);

        // Http 远程调用
        Map<String, Object> resp = WebClient.create()
                .method(HttpMethod.POST)
                .uri(uriWithParams)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .bodyValue(bodyData.toString())
                .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {})
                .block();

        //        // 临时数据 (模拟成功)
        //        Map<String, Object> tempPayload = new LinkedHashMap<>();
        //        tempPayload.put("StageInfo", "{\"TrainResult\":[\"url1\",\"url2\"],\"RejectReason\":\"\",\"TrainRecord\":[{\"StartTime\":\"2023-11-01 23:00:17\",\"Record\":[\"url1\",\"url2\"]}]}");
        //        tempPayload.put("Status", "MAKING");
        //        tempPayload.put("StatusV2", "WAIT_PROCESS");
        //        tempPayload.put("FailMessage", "");
        //        tempPayload.put("StageV2", "CONFIRM");
        //        resp.put("Payload", tempPayload);

        System.out.println("getProgressOfVirtualmanTask:");
        System.out.println(resp);

        // 判断 任务是否查询成功 (是，则更新数据库记录)
        Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
        if (respHeaderCode == 0) {

            // 任务失败
            String respPayloadStage = (String) MapUtil.get(resp, "Payload.Stage");
            String respPayloadStageInfo = (String) MapUtil.get(resp, "Payload.StageInfo");

            System.out.println("-- respPayloadStageInfo --------");
            System.out.println(respPayloadStageInfo);
            System.out.println("--------------------------------");
//            if (respPayloadStage.equals("END")) {
//                throw new RuntimeException(respPayloadStageInfo);
//            }

            String respPayloadStatus = (String) MapUtil.get(resp, "Payload.Status");

            // 定制任务完成后，
            // - 失败，记得把状态改为 1未使用
            // - 成功，记得把 quota 状态改为 3已使用
            AiivhQuotaDTO aiivhQuotaDTO = new AiivhQuotaDTO();
            if ("FAIL".equals(respPayloadStatus)) {
                aiivhQuotaDTO.setQuota_status(1);
            }
            if ("SUCCESS".equals(respPayloadStatus)) {
                aiivhQuotaDTO.setQuota_status(3);
            }
            aiivhQuotaMapper.updateAiivhQuota(aiivhQuotaDTO);

//            String respPayloadFailMessage = (String) MapUtil.get(resp, "Payload.FailMessage");

            /**
             * 更新任务
             */
            AiPersonDTO aiPersonDTO = new AiPersonDTO();
            // [查询] 自身 UserId
            Long user_id = httpRequestAspect.getUserId();
            aiPersonDTO.setUser_id(user_id);
            aiPersonDTO.setTask_id(task_id);
            aiPersonDTO.setTask_status(respPayloadStatus);
            aiPersonDTO.setTask_status_info(respPayloadStageInfo);
            aiPersonDTO.setStage_status(respPayloadStage);  // 返回值 { Payload } 跟文档不一样，文档是 { StageV2 }，实际上是返回 { Stage }

            // 返回值 { StageInfo } 跟文档不一样，文档是：
            // {\"TrainResult\":[\"url1\",\"url2\"],\"RejectReason\":\"\",\"TrainRecord\":[{\"StartTime\":\"2023-11-01 23:00:17\",\"Record\":[\"url1\",\"url2\"]}]}
            // 实际上是：
            // {"TrainResult":["https://videomaker-resources.ivh.qq.com/broadcast/Basic/video/78f7fb6c6fec4311921ede313f3c7cd5.mp4","https://videomaker-resources.ivh.qq.com/broadcast/Basic/video/259044134def40f69fdfdc1cd6c6c952.mp4"],"RejectReason":"","TrainRecord":[{"StartTime":"2024-03-21 18:06:59","Record":["https://videomaker-resources.ivh.qq.com/broadcast/Basic/video/78f7fb6c6fec4311921ede313f3c7cd5.mp4","https://videomaker-resources.ivh.qq.com/broadcast/Basic/video/259044134def40f69fdfdc1cd6c6c952.mp4"]}]}
            if (!respPayloadStageInfo.isEmpty()) {
                JSONObject StageInfoObject = JSONUtil.parseObj(respPayloadStageInfo);
                String result = JSONUtil.toJsonStr(StageInfoObject);
                aiPersonDTO.setResult(result);
            }

            System.out.println("aiPersonDTO:");
            System.out.println(aiPersonDTO);

            // [Post] 根据 Payload 返回的状态来更新
            aiivhMakeVirtualmanTaskService.updateAiivhMakeVirtualmanTask(aiPersonDTO);

        }

        return resp;
    }

    @Transactional(rollbackFor = Exception.class)
    public List<Map<String, Object>> getQueueOfVirtualmanTask(String task_id) {

        RList<String> rList = redissonClient.getList("makeVirtualTaskQueue");
        List<String> queueList = rList.readAll();

        List<Map<String, Object>> result = new ArrayList<>();
        if (queueList != null && queueList.size() > 0) {
            for (String queue : queueList) {
                result.add(JSONUtil.parseObj(queue));
            }
        }

        return result;
    }

    /**
     * [腾讯-数智人] 确认形象/音色定制效果
     */
    @Transactional
    public Map<String, Object> confirmVirtualmanTask(TencentCustomserviceConfirmDTO tencentCustomserviceConfirmDTO) {

        try {
            TencentCustomserviceConfirmPlayloadDTO payload = (TencentCustomserviceConfirmPlayloadDTO) tencentCustomserviceConfirmDTO.getPayload();

            // 基于实体类，将 anchor_name 转换为 AnchorName (将下划线转为大驼峰)
            Map<String, Object> resultMap = new HashMap<>();
            convertObjectToMap(tencentCustomserviceConfirmDTO, resultMap);
            String bodyData = JSONUtil.toJsonStr(resultMap);

            // -- Http Request --------------------------------------------
            Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken); // 获得 Signature 签名

            String url = "https://gw.tvs.qq.com/v2/ivh/assetmanager/customservice/customerconfirm";
            URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                    .queryParam("appkey", signature.get("appkey"))
                    .queryParam("signature", "{signature}")
                    .queryParam("timestamp", signature.get("timestamp"))
                    .build(signature.get("signature"));

            // Http 远程调用
            Map<String, Object> resp = WebClient.create()
                    .method(HttpMethod.POST)
                    .uri(uriWithParams)
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                    .bodyValue(bodyData)
                    .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
                    })
                    .block();

            System.out.println(resp);


//            // 临时数据 (模拟成功)
//            Map<String, Object> tempPayload = new LinkedHashMap<>();
//            tempPayload.put("Operate", "AGREE");
////            tempPayload.put("Operate", "REJECT");
////            tempPayload.put("Reason", "REJECT 就是 拒绝");
//            resp.put("Payload", tempPayload);


            // 判断 任务是否确认成功 (是，则更新数据库记录)
            Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
            if (respHeaderCode == 0) {

                AiPersonDTO aiPersonDTO = new AiPersonDTO();
                // [查询] 自身 UserId
                Long user_id = httpRequestAspect.getUserId();
                aiPersonDTO.setUser_id(user_id);

                aiPersonDTO.setTask_id(payload.getTask_id());

                String Operate = (String) MapUtil.get(resp, "Payload.Operate");
                aiPersonDTO.setConfirm_status(Operate);
                aiPersonDTO.setConfirm_status_info((String) MapUtil.get(resp, "Payload.Reason"));

                /**
                 * 更新定制数智人任务
                 */
                aiivhMakeVirtualmanTaskService.updateAiivhMakeVirtualmanTask(aiPersonDTO);

                /**
                 * 更新形象 (仅更新 stage_status)
                 */
                if (Operate == "AGREE") {

                    AiivhAnchorDTO aiivhAnchorDTO = new AiivhAnchorDTO();
                    //Map<String, Object> detail = aiPersonMapper.queryAiPersonDetail(aiPersonDTO);
                    Map<String, Object> detail = aiivhMakeVirtualmanTaskService.queryAiivhMakeVirtualmanTaskDetail(aiPersonDTO);
                    aiivhAnchorDTO.setUser_id(user_id);
                    aiivhAnchorDTO.setAnchor_name((String) detail.get("anchor_name"));
                    aiivhAnchorDTO.setStage_status((String) detail.get("stage_status"));
                    System.out.println(aiivhAnchorDTO);

                    aiivhMakeVirtualmanTaskService.updateAiPersoneVirtualmanCustom(aiivhAnchorDTO);

                }

            }

            return resp;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }





//    /**
//     * 腾讯-数智人-个人资产管理-分页查询小样本形象列表接口
//     * https://cloud.tencent.com/document/product/1240/93404
//     */
//    @Transactional
//    public Map<String, Object> getCustomVirtualman(Integer pageNum, Integer pageSize) {
//        Map<String, Object> payload = new HashMap<>();
//        payload.put("VirtualmanTypeCode", "small_sample_2d");
////        String[] anchor_codes = new String[]{"imagetrain_100035906086_3416"};
////        payload.put("AnchorCodes", anchor_codes);
//        payload.put("PageIndex", pageNum);
//        payload.put("PageSize", pageSize);
//
//
//        Map<String, Object> resultMap = Collections.singletonMap("Payload", payload);
//        String bodyData = JSONUtil.toJsonStr(resultMap);
//
//        // -- Http Request --------------------------------------------
//        /**
//         * 这里使用我自己的空白密钥，因为只有未创建自定义形象的账号，才能返回全部形象
//         */
//        Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken); // 获得 Signature 签名
//
//        String url = "https://gw.tvs.qq.com/v2/ivh/crmserver/customerassetservice/describesmallsampleimage";
//        URI uriWithParams = UriComponentsBuilder.fromUriString(url)
//                .queryParam("appkey", signature.get("appkey"))
//                .queryParam("signature", "{signature}")
//                .queryParam("timestamp", signature.get("timestamp"))
//                .build(signature.get("signature"));
//
//        // Http 远程调用
//        Map<String, Object> resp = WebClient.create()
//                .method(HttpMethod.POST)
//                .uri(uriWithParams)
//                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
//                .bodyValue(bodyData.toString())
//                .retrieve().bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
//                })
//                .block();
//
//        Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
//        if (respHeaderCode == 0) {
//            List<Map<String, Object>> virtualmans = (List<Map<String, Object>>) MapUtil.get(resp, "Payload.Virtualmans");
//            Map<String, Object> response = new LinkedHashMap<>();
//            // 遍历 virtualmans 列表，并将 key 由大驼峰改为下划线命名
//            virtualmans = ListUtil.convertToUnderscoreCase(virtualmans);
//
//
//            // 分页查询
//            if (pageNum != null && pageSize != null) {
//                PageHelper.startPage(pageNum, pageSize);
//                PageHelper.getLocalPage().setPageSizeZero(true);
//            }
//            PageInfoResult pageInfoResult = new PageInfoResult(virtualmans);
//            return pageInfoResult.toMap();
//        }
//
//        return resp;
//    }

    
    /**
     * 获得全部形象列表 (循环调用接口，至到获得全部数据)
     * List<Map<String, Object>> virtualmans = getAllVirtualman(pageIndex, pageSize);
     */
    private List<Map<String, Object>> getAllVirtualmanNoEmpty(Integer pageIndex, Integer pageSize) {

        // 初始化变量
        List<Map<String, Object>> timbre_assets = new ArrayList<>();

        // 第一次调用 initTimbre 方法，并将当前页的数据追加到 timbre_assets 列表中
        Map<String, Object> resp = initCustomVirtualman(pageIndex, pageSize);
        Integer total = (Integer) MapUtil.get(resp, "Payload.Total");
        timbre_assets.addAll((List<Map<String, Object>>) MapUtil.get(resp, "Payload.Virtualmans"));

        // 循环获取所有页的数据
        while (total != null && total > 0 && pageIndex * pageSize <= total) {
            // 准备下一次查询，页码加1
            pageIndex++;
            System.out.println("pageIndex: " + pageIndex);

            // 再次调用 initTimbre 方法获取下一页的数据，并将当前页的数据追加到 timbre_assets 列表中
            resp = initCustomVirtualman(pageIndex, pageSize);
            total = (Integer) MapUtil.get(resp, "Payload.Total");
            timbre_assets.addAll((List<Map<String, Object>>) MapUtil.get(resp, "Payload.Virtualmans"));
        }
        return timbre_assets;
    }

    /**
     * [内部服务] 获得形象详细信息 (通过音色名称)
     */
    @Transactional
    public Map<String, Object> getVirtualmanObject(String anchor_name) {

        // 循环获得数据列表，直到 total 小于 pageSize
        List<Map<String, Object>> virtualmans = getAllVirtualmanNoEmpty(1, 100);

        // 遍历 virtualmans 列表，并将 key 由大驼峰改为下划线命名
        virtualmans = ListUtil.convertToUnderscoreCase(virtualmans);

        // 遍历 匹配 anchor_name
        Map<String, Object> result = new LinkedHashMap<>();
        for (Map<String, Object> virtualman : virtualmans) {
            String virtualman_anchor_name = (String) virtualman.get("anchor_name");
            // System.out.println("virtualman_anchor_name: " + virtualman_anchor_name + ", anchor_name: " + anchor_name);
            if (virtualman_anchor_name.equals(anchor_name)) {
                result = virtualman;
                System.out.println(virtualman);
            }
        }
        return result;

    }

}
