package com.backendsys.service.Ai.Aiivh;

import cn.hutool.core.util.StrUtil;

import com.backendsys.aspect.HttpRequestAspect;
import com.backendsys.entity.Ai.Aiivh.AiivhMakeBroadcastTask.*;
import com.backendsys.exception.CustException;
import com.backendsys.mapper.Ai.Aiivh.AiivhAnchorMapper;
import com.backendsys.mapper.Ai.Aiivh.AiivhMakeBroadcastTaskMapper;
import com.backendsys.service.SDKService.SDKTencent.SDKTencentAiVideoService;
import com.backendsys.utils.MapUtil;
import com.backendsys.utils.ResourceUtil;
import com.backendsys.utils.response.PageInfoResult;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.awt.*;
import java.io.IOException;
import java.util.*;
import java.util.List;

@Service
public class AiivhMakeBroadcastTaskServiceImpl implements AiivhMakeBroadcastTaskService {

    // 引用配置变量
    @Value("${HTTP_BASE_URI}")
    private String baseUrl;

    @Autowired
    private HttpRequestAspect httpRequestAspect;

    @Autowired
    private AiivhAnchorMapper aiivhAnchorMapper;
    @Autowired
    private SDKTencentAiVideoService sdkTencentAiVideoService;

    @Autowired
    private AiivhMakeBroadcastTaskMapper aiivhMakeBroadcastTaskMapper;


    @Override
    public Map<String, Object> queryAiivhMakeBroadcastTaskList(Integer pageNum, Integer pageSize, AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO) {
        // 分页查询
        if (pageNum != null && pageSize != null) {
            PageHelper.startPage(pageNum, pageSize);
            PageHelper.getLocalPage().setPageSizeZero(true);
        }
        // 分页输出 (自定义)
        List<Map<String, Object>> list = aiivhMakeBroadcastTaskMapper.queryAiivhMakeBroadcastTaskList(aiivhMakeBroadcastTaskDTO);

        PageInfoResult pageInfoResult = new PageInfoResult(list);
        return pageInfoResult.toMap();
    }

    @Override
    public Map<String, Object> queryAiivhMakeBroadcastTaskDetail(AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO) {

        Map<String, Object> detail = aiivhMakeBroadcastTaskMapper.queryAiivhMakeBroadcastTaskDetail(aiivhMakeBroadcastTaskDTO);
        if (detail == null) {
            throw new CustException("(task_id) 找不到任务");
        }
        if (!aiivhMakeBroadcastTaskDTO.getUser_id().equals((Long) detail.get("user_id"))) {
            throw new CustException("(task_id) 任务没有访问权限");
        }

        // -- 如果是任务 CONFIRM 状态，则刷新状态
        String task_status = (String) detail.get("task_status");
        if (task_status.equals("MAKING") || task_status.equals("COMMIT")) {

            // 访问详情的同时，刷新一次进度
            String task_id = aiivhMakeBroadcastTaskDTO.getTask_id();
            Map<String, Object> resp = sdkTencentAiVideoService.customPersonvideoGetprogress(task_id);
            System.out.println("customPersonvideoGetprogress:");
            System.out.println(resp);

            Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
            if (respHeaderCode == 0) {
                Map<String, Object> vo = new LinkedHashMap<>();
                vo.put("task_id", task_id);
                vo.put("request_id", (String) MapUtil.get(resp, "Header.RequestID"));
                vo.put("task_status", (String) MapUtil.get(resp, "Payload.Status"));
                vo.put("task_reason", (String) MapUtil.get(resp, "Payload.FailMessage"));
                vo.put("result_media_url", (String) MapUtil.get(resp, "Payload.MediaUrl"));
                vo.put("result_subitles_url", (String) MapUtil.get(resp, "Payload.SubtitlesUrl"));
                aiivhMakeBroadcastTaskMapper.updateAiivhMakeBroadcast(vo);
            }
        }

        return detail;
    }


    /**
     * 封装方法 仅在当前使用
     * 将多层级的对象，转换为数据表字段所支持的字段，比如：speech_param.speed 转为 speech_speed
     * Map<String, Object> aiPersonvideoCustomPO = dtoToMap(aiivhMakeBroadcastTaskDTO)
     */
    private Map<String, Object> dtoToVo(AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO, Map<String, Object> virtualman_detail) throws IOException {
        Map<String, Object> po = new LinkedHashMap<>();

        po.put("user_id", aiivhMakeBroadcastTaskDTO.getUser_id());
        po.put("request_id", aiivhMakeBroadcastTaskDTO.getRequest_id());
        po.put("task_id", aiivhMakeBroadcastTaskDTO.getTask_id());
        po.put("virtualman_key", aiivhMakeBroadcastTaskDTO.getVirtualman_key());
        po.put("title", aiivhMakeBroadcastTaskDTO.getTitle());
        po.put("driver_type", aiivhMakeBroadcastTaskDTO.getDriver_type());
        po.put("input_ssml", aiivhMakeBroadcastTaskDTO.getInput_ssml());
        po.put("input_audio_url", aiivhMakeBroadcastTaskDTO.getInput_audio_url());

        SpeechParamDTO speech_param = aiivhMakeBroadcastTaskDTO.getSpeech_param();
        if (speech_param != null) {
            po.put("speech_speed", speech_param.getSpeed());
            po.put("speech_timbre_key", speech_param.getTimbre_key());
            po.put("speech_volume", speech_param.getVolume());
        }

        VideoParamDTO video_param = aiivhMakeBroadcastTaskDTO.getVideo_param();
        if (video_param != null) {
            po.put("video_format", video_param.getFormat());
            po.put("video_background_file_url", video_param.getBackground_file_url());
            po.put("video_head_file_url", video_param.getHead_file_url());
            po.put("video_tail_file_url", video_param.getTail_file_url());
            po.put("video_show_subtitles", video_param.getShow_subtitles());
            po.put("video_smart_action_enabled", video_param.getSmart_action_enabled());

            List<VideoParamLogoParamDTO> logo_params = video_param.getLogo_params();

            if (logo_params != null && logo_params.size() > 0) {
                po.put("video_logo_file_url", logo_params.get(0).getLogo_file_url());
                po.put("video_logo_position_x", logo_params.get(0).getPosition_x());
                po.put("video_logo_position_y", logo_params.get(0).getPosition_y());
                po.put("video_logo_scale", logo_params.get(0).getScale());
            }

            VideoParamAnchorParamDTO anchor_param = video_param.getAnchor_param();
            if (anchor_param != null) {


                // 当查询到形象详情时
                if (virtualman_detail != null) {
                    // [形象大小] 默认 1
                    Float scale = anchor_param.getScale();
                    po.put("video_anchor_scale", scale);


                    // 获取 [形象] 远程图片的宽高
                    String poseImageUrl = (String) virtualman_detail.get("pose_image");
                    Dimension poseImageSize = ResourceUtil.getRemoteImageSize(poseImageUrl);
//                    System.out.println("anchor width: " + poseImageSize.width + ", anchor height: " + poseImageSize.height);
                    int anchorWidth = poseImageSize.width;
                    int anchorHeight = poseImageSize.height;

//                    // [形象分辨率] 将 "1080x1920" 转为 1080 和 1920
//                    String resolution = (String) virtualman_detail.get("resolution");
//                    String[] dimensions = resolution.split("x");
//                    int anchorWidth = Integer.parseInt(dimensions[0]);
//                    int anchorHeight = Integer.parseInt(dimensions[1]);
//                    // [容器大小] 一般情况下 container 容器大小 与 anchor 形象大小 一致;
//                    int containerWidth = anchorWidth;
//                    int containerHeight = anchorHeight;

                    // 获取 [背景] 远程图片的宽高
                    String backgroundImageUrl = video_param.getBackground_file_url();
                    Dimension backgroundImageSize = ResourceUtil.getRemoteImageSize(backgroundImageUrl);
//                    System.out.println(backgroundImageSize);
//                    System.out.println("background width: " + backgroundImageSize.width + ", background height: " + backgroundImageSize.height);
                    int containerWidth = backgroundImageSize != null ? backgroundImageSize.width : anchorWidth;
                    int containerHeight = backgroundImageSize != null ? backgroundImageSize.height : anchorHeight;

                    // X/Y 原始值 (绝对值)
                    Float horizontal_position_x = anchor_param.getHorizontal_position() == null ? 0 : Math.abs(anchor_param.getHorizontal_position());
                    Float vertical_position_y = anchor_param.getVertical_position() == null ? 0 : Math.abs(anchor_param.getVertical_position());

                    Float horizontal_position = (horizontal_position_x == 0 || horizontal_position_x == null) ? 0 : Math.abs((horizontal_position_x + (anchorWidth * scale) / 2 - containerWidth / 2) / containerWidth);
                    Float vertical_position = (vertical_position_y == 0 || vertical_position_y == null) ? 0 : Math.abs(((anchorHeight * scale) + vertical_position_y - containerHeight) / 2 / containerHeight);
//                    Float horizontal_position = Math.abs((horizontal_position_x + (anchorWidth * scale) / 2 - containerWidth / 2) / containerWidth);
//                    Float vertical_position = Math.abs(((anchorHeight * scale) + vertical_position_y - containerHeight) / 2 / containerHeight);

                    // X/Y 表达式计算出来的结果 (绝对值)
                    System.out.println("horizontal_position:");
                    System.out.println("(" + horizontal_position_x + " + (" + anchorWidth + " * " + scale + ") / 2 - " + containerWidth + "/ 2) / " + containerWidth + ") = " + horizontal_position);
                    System.out.println("vertical_position:");
                    System.out.println("(" + anchorHeight + " * " + scale + ") + " + vertical_position_y + " - " + containerHeight + ") / 2 / " + containerHeight + ") = " + vertical_position);
                    System.out.println("--------------------------------------------------------");

                    // 判断 X 原始值是否为负数，是则赋予负值
                    Boolean is_horizontal_negative = anchor_param.getHorizontal_position() != null && anchor_param.getHorizontal_position() < 0;
                    Boolean is_vertical_negative = anchor_param.getVertical_position() != null && anchor_param.getVertical_position() < 0;
                    if (is_horizontal_negative) horizontal_position = -horizontal_position;
                    if (is_vertical_negative) vertical_position = -vertical_position;

                    po.put("video_anchor_horizontal_position_x", horizontal_position_x);    // 原始 x 值
                    po.put("video_anchor_vertical_position_y", vertical_position_y);        // 原始 y 值

                    po.put("video_anchor_horizontal_position", horizontal_position);        // 表达式计算 x 出来的结果
                    po.put("video_anchor_vertical_position", vertical_position);            // 表达式计算 y 出来的结果

                    System.out.println("---------------------");
                    System.out.println("anchorWidth: " + anchorWidth + ", anchorHeight: " + anchorHeight + ", scale: " + scale);
                    System.out.println("containerWidth: " + containerWidth + ", containerHeight: " + containerHeight);
                    System.out.println("(horizontal水平) 原始X: " + horizontal_position_x + ", 计算X: " + horizontal_position);
                    System.out.println("(vertical垂直) 原始Y: " + vertical_position_y + ", 计算Y: " + vertical_position);
                    System.out.println("---------------------");

                    anchor_param.setHorizontal_position_x(horizontal_position_x);
                    anchor_param.setVertical_position_y(vertical_position_y);

                    anchor_param.setHorizontal_position(horizontal_position);
                    anchor_param.setVertical_position(vertical_position);

                    /**
                     位置计算公式：

                     参数1：HorizontalPosition：主播横向位置（默认值是0，负数为向左调整，正数为向右调整）
                     定义：屏幕中轴线距形象中轴线距离/整个屏幕宽度；
                     公式 : HorizontalPositon = (x + anchorWidth / 2 - containerWidth / 2) / containerWidth
                     -----------------------------------------------------------------------------------------
                     参数2：VerticalPositon：主播纵向位置（默认值是0，负数为向上调整，正数为向下调整)
                     定义：形象底部距屏幕底部距离/两倍形象高度
                     公式 : VerticalPositon=(anchorHeight + y - wrapperHeight) / 2 / anchorHeight
                     -----------------------------------------------------------------------------------------
                     参数3：Scale：主播大小（默认值是：1；数值越小，主播越小)
                     缩放规则：左下顶点保持不动，按照形象宽度、形象高度等比例缩放。
                     -----------------------------------------------------------------------------------------
                     相关参数的定义：
                     ① anchorHeight：即为叠加了缩放系数的形象高度，即 “原始图片高度” originzoomScale
                     ② anchorWidth：即为叠加了缩放系数的形象宽度，即 “原始图片宽度” originzoomScale
                     ③ 原始图片高度、宽度：即《个人资产管理 apaas 接口协议》→4.1分页查询小样本形象列表接口中，Poselmage 的高度和宽度。
                     */

                }

            }
        }

        return po;
    }

    /**
     * 文档中心 > 腾讯云智能数智人 > API 接入 > 视频生成服务 API 文档-v4.9.7 > 视频制作接口-进阶版
     * https://cloud.tencent.com/document/product/1240/81269#ec658a0e-cd14-48c2-af38-9ca07936a513
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> insertAiivhMakeBroadcastTask(AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO) throws IOException {

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

        // [设置] 回调地址
        // https://cloud.tencent.com/document/product/1240/81279
        aiivhMakeBroadcastTaskDTO.setCallback_url(baseUrl + "/api/public/ai/ivh/makeBroadcastCallback");

        // 【入参校验】
        // ---------------------------------------------------------------------------
        // [判断] 驱动类型 (Text 文本驱动 | OriginalVoice 原声音频驱动 | ModulatedVoice 变声音频驱动)
        String driver_type = aiivhMakeBroadcastTaskDTO.getDriver_type();
        if (StrUtil.isBlank(driver_type) || driver_type.equals("Text")) {
            // -- 当 DriverType 为空 或 Text 时，{ input_ssml } 字段必填 ------------------------------
            String input_ssml = aiivhMakeBroadcastTaskDTO.getInput_ssml();
            if (StrUtil.isBlank(input_ssml)) {
                throw new CustException("当 {driver_type} 为空 或 {driver_type} 为 Text 时，{input_ssml} 必填");
            }
        } else if (driver_type.equals("OriginalVoice") || driver_type.equals("ModulatedVoice")) {
            // -- 当 DriverType 为 OriginalVoice、ModulatedVoice 时，{ input_audio_url } 字段必填 -----
            String input_audio_url = aiivhMakeBroadcastTaskDTO.getInput_audio_url();
            if (StrUtil.isBlank(input_audio_url)) {
                throw new CustException("当 {driver_type} 为 OriginalVoice 或 ModulatedVoice 时，{input_audio_url} 必填");
            }
        }
        // ---------------------------------------------------------------------------
        SpeechParamDTO speech_param = aiivhMakeBroadcastTaskDTO.getSpeech_param();

        // --- [video_param] -------------------------------------------------------------
        VideoParamDTO video_param = aiivhMakeBroadcastTaskDTO.getVideo_param();
        if (video_param != null) {
            // [判断] video_param.background_file_url 文件类型
            String videoparam_background_file_url = video_param.getBackground_file_url();
            String[] videoparam_background_file_url_suffix = new String[]{"jpg", "png", "mp4"};
            if (!StrUtil.isBlank(videoparam_background_file_url) && !ResourceUtil.checkUrlSuffix(videoparam_background_file_url, videoparam_background_file_url_suffix)) {
                throw new CustException("字段 {video_param.background_file_url} 格式必须是 " + Arrays.toString(videoparam_background_file_url_suffix));
            }
            // ---------------------------------------------------------------------------
            // [判断] video_param.head_file_url、video_param.tail_file_url 文件类型
            String videoparam_head_file_url = video_param.getHead_file_url();
            String videoparam_tail_file_url = video_param.getTail_file_url();
            String[] videoparam_file_url_suffix = new String[]{"mp4"};
            if (!StrUtil.isBlank(videoparam_head_file_url) && !ResourceUtil.checkUrlSuffix(videoparam_head_file_url, videoparam_file_url_suffix)) {
                throw new CustException("字段 {video_param.head_file_url} 格式必须是 " + Arrays.toString(videoparam_file_url_suffix));
            }
            if (!StrUtil.isBlank(videoparam_tail_file_url) && !ResourceUtil.checkUrlSuffix(videoparam_tail_file_url, videoparam_file_url_suffix)) {
                throw new CustException("字段 {video_param.tail_file_url} 格式必须是 " + Arrays.toString(videoparam_file_url_suffix));
            }
            // ---------------------------------------------------------------------------

            // --- [logo_params] --------------------------------------------------------------
            List<VideoParamLogoParamDTO> logo_params = video_param.getLogo_params();
            if (logo_params != null && logo_params.size() > 0) {
                // [判断] video_param.logo_params.file_url 文件类型
                String logo_param_file_url = logo_params.get(0).getLogo_file_url();
                String[] logoparam_file_url_suffix = new String[]{"jpg", "png"};
                if (!StrUtil.isBlank(logo_param_file_url) && !ResourceUtil.checkUrlSuffix(logo_param_file_url, logoparam_file_url_suffix)) {
                    throw new CustException("字段 {video_param.logo_params.file_url} 格式必须是 " + Arrays.toString(logoparam_file_url_suffix));
                }
            }

            // --- [anchor_param] ------------------------------------------------------------
            VideoParamAnchorParamDTO anchor_param = video_param.getAnchor_param();
            if (anchor_param != null) {}
        }


        // 获得形象Key的详细信息 (获得形象分辨率)
        String virtualman_key = aiivhMakeBroadcastTaskDTO.getVirtualman_key();
        Map<String, Object> virtualman_detail = aiivhAnchorMapper.queryAiivhAnchorDetail(virtualman_key, user_id);


        // 【腾讯云接口】
        /*
        CallbackUrl
        当用户增加回调url时，将把视频制作结果以固定格式发送post请求到该url地址，固定格式见 附录二： 回调请求体格式。
        需注意：
        1. 限制CallbackUrl长度小于1000
        2. 只发送一次请求，无论是哪种问题导致的请求失败，都不会再进行发送。
         */
        Map<String, Object> vo = dtoToVo(aiivhMakeBroadcastTaskDTO, virtualman_detail);

        System.out.println("vo:");
        System.out.println(vo);
        System.out.println("----------------------------------------");

        // 文档中心 > 腾讯云智能数智人 > 服务端 API 接入 > 视频生成服务 API 文档-v4.9.7 > 视频制作接口-进阶版
        // https://cloud.tencent.com/document/product/1240/81269

        Map<String, Object> resp = sdkTencentAiVideoService.customPersonvideoMake(aiivhMakeBroadcastTaskDTO);
        Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
        if (respHeaderCode != null && respHeaderCode != 0) {
            String respHeaderMessage = (String) MapUtil.get(resp, "Header.Message");
            throw new CustException("(" + respHeaderCode + ") " + respHeaderMessage);
        }
        String task_id = (String) MapUtil.get(resp, "Payload.TaskId");
        vo.put("task_id", task_id);
        vo.put("request_id", (String) MapUtil.get(resp, "Header.RequestID"));

        long insert = aiivhMakeBroadcastTaskMapper.insertAiivhMakeBroadcast(vo);
        if (insert == 1) {
            return Map.of("task_id", task_id);
        }

        return null;
    }

    /**
     * 当用户增加回调url时，将把视频制作结果以固定格式发送post请求到该url地址，固定格式见 附录二： 回调请求体格式。
     * 需注意：
     * 1. 限制CallbackUrl长度小于1000
     * 2. 只发送一次请求，无论是哪种问题导致的请求失败，都不会再进行发送。
     * https://cloud.tencent.com/document/product/1240/81269
     * https://cloud.tencent.com/document/product/1240/81279
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> updateAiivhMakeBroadcastTask(AiivhMakeBroadcastTaskCallbackDTO aiivhMakeBroadcastTaskCallbackDTO) {

        System.out.println("(updateAiivhMakeBroadcastTask) 回调");
        System.out.println(aiivhMakeBroadcastTaskCallbackDTO);

        String task_id = aiivhMakeBroadcastTaskCallbackDTO.getPayload().getTaskId();

        AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO = new AiivhMakeBroadcastTaskDTO();
        aiivhMakeBroadcastTaskDTO.setTask_id(task_id);
        Map<String, Object> detail = aiivhMakeBroadcastTaskMapper.queryAiivhMakeBroadcastTaskDetail(aiivhMakeBroadcastTaskDTO);

        if (detail == null) {
            throw new CustException("(task_id) 找不到任务");
        } else {
            String task_status = (String) detail.get("task_status");
            if (!task_status.equals("CONFIRM")) {
                throw new CustException("(task_id) 任务结果不可重复更新");
            }
        }

        // 判断如果该任务为 "CONFIRM"，则作出更新 (如果已经更新，则不会变化)
        Map<String, Object> vo = new LinkedHashMap<>();
        vo.put("task_id", task_id);
        vo.put("task_status", aiivhMakeBroadcastTaskCallbackDTO.getPayload().getStatus());
        vo.put("task_reason", aiivhMakeBroadcastTaskCallbackDTO.getPayload().getFailMessage());
        vo.put("result_media_url", aiivhMakeBroadcastTaskCallbackDTO.getPayload().getMediaUrl());
        vo.put("result_subitles_url", aiivhMakeBroadcastTaskCallbackDTO.getPayload().getSubtitlesUrl());
        long update = aiivhMakeBroadcastTaskMapper.updateAiivhMakeBroadcast(vo);
        if (update == 1) {
            return Map.of("task_id", task_id);
        } else {
            System.out.println("(aiPersonvideoMapper.updateAiivhMakeBroadcast) 更新失败");
            System.out.println(vo);
        }

        return null;
    }


    /**
     * 删除
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> deleteAiivhMakeBroadcastTask(AiivhMakeBroadcastTaskDTO aiivhMakeBroadcastTaskDTO) {
        aiivhMakeBroadcastTaskMapper.deleteAiivhMakeBroadcast(aiivhMakeBroadcastTaskDTO);
        return Map.of("task_id", aiivhMakeBroadcastTaskDTO.getTask_id());
    }
}
