package com.backendsys.controller.Ai;

import com.backendsys.aspect.HttpRequestAspect;
import com.backendsys.entity.Ai.Aiivh.*;
import com.backendsys.entity.PageDTO;
import com.backendsys.mapper.Ai.Aiivh.AiivhTimbreEmotionalCategoryMapper;
import com.backendsys.service.Ai.AiPersonTimbreService;
import com.backendsys.service.Ai.Aiivh.AiivhAnchorService;
import com.backendsys.service.Ai.Aiivh.AiivhAssetService;
import com.backendsys.service.SDKService.SDKTencent.SDKTencentAiService;
import com.backendsys.service.SDKService.SDKTencent.SDKTencentCOSService;
import com.backendsys.utils.response.Result;
import com.backendsys.utils.response.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;

@RestController
public class AiivhAssetController {

    @Autowired
    private HttpRequestAspect httpRequestAspect;

    @Autowired
    private AiivhAssetService aiivhAssetService;

    @Autowired
    private AiivhAnchorService aiivhAnchorService;

    @Autowired
    private AiPersonTimbreService aiPersonTimbreService;

    @Autowired
    private AiivhTimbreEmotionalCategoryMapper aiivhTimbreEmotionalCategoryMapper;

    @Autowired
    private SDKTencentAiService sdkTencentAiService;

    @Autowired
    private SDKTencentCOSService sdkTencentCOSService;


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

    /**
     * 上传方式一：接口返回临时密钥，由前端使用 JSSDK 上传
     *
     * 获得临时上传的密钥对 (给前端)
     */
    @GetMapping("/api/ai/ivh/asset/getUploadAuthorization")
    public Result getUploadAuthorization() {
        return Result.success(sdkTencentCOSService.getIvhTempCredentials(appKey, accessToken));
    }

    /**
     * 上传方式二：对接腾讯云 COS 的 SDK，调用其中的 putObject 方法将素材上传，上传时需要用到获取到的上传地址和上传凭证。
     * 注：因为大文件无法分块上传，改成前端 JSSDK 上传，效率更高
     *
     * 文档中心 > 腾讯云智能数智人 > API 接入 > 2D真人小样本形象定制 API 文档-v0.2.1 > 上传素材文件到云端存储 > 上传素材到腾讯云 COS
     * https://cloud.tencent.com/document/product/1240/96068
     */
    @PostMapping("/api/ai/ivh/asset/uploadFile")
    public Result uploadFile(@RequestParam(value = "file", required = false) MultipartFile file,
                         @RequestParam(value = "type", required = false, defaultValue = "") String type
    ) throws UnsupportedEncodingException {

        // -- 参数校验 --------------------------------------------------------------------------
        // 判断上传的文件是否为空
        if (file == null || file.isEmpty()) {
            return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), "上传文件 (file) 不能为空");
        }
        if (type == "" || type == null || type.isEmpty()) {
            return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), "上传类型 (type) 不能为空");
        }
        // 上传的目标路径
        String fileTargetPath;
        // 当前上传的文件名
        String fileErrorMessage = null;

        // 通过判断 type 类型，来初始化 URL 末端的 文件路径和文件名 (fileTargetPath):
        // - idcardMp4: /idcard/a.mp4                   视频格式授权书 (不超1GB)
        // - idcardPdf: /idcard/b.pdf                   PDF格式授权书 (不超10MB)
        // - materialVideo:  /video/c.mp4 | .mov        形象定制素材 (不超5GB)
        // - materialAudioZip:  /audio/c.zip            声音定制素材 (不超1GB)
        // - materialPhotoPNG:  /photo/example.png      照片数智人定制素材 (不超10MB)
        // - materialPhotoJPG:  /photo/example.jpg      照片数智人定制素材 (不超10MB)
        // - audio: wav、mp3、wma、m4a、aac

        switch(type) {
            case "idcardMp4":
                fileErrorMessage = checkFile(file, new String[]{".mp4",".mov"}, 1L * 1024 * 1024 * 1024, "GB");
                fileTargetPath = "idcard/a.mp4";
                break;
            case "idcardPdf":
                fileErrorMessage = checkFile(file, new String[]{".pdf"}, 10L * 1024 * 1024, "MB");
                fileTargetPath = "idcard/b.pdf";
                break;
            case "materialVideo":
                fileErrorMessage = checkFile(file, new String[]{".mp4",".mov"}, 5L * 1024 * 1024 * 1024, "GB");
                fileTargetPath = "video/c.mp4";
                break;
            case "materialAudioZip":
                fileErrorMessage = checkFile(file, new String[]{".zip"}, 1L * 1024 * 1024 * 1024, "GB");
                fileTargetPath = "audio/c.zip";
                break;
            case "materialPhotoPNG":
                fileErrorMessage = checkFile(file, new String[]{".png"}, 10L * 1024 * 1024, "MB");
                fileTargetPath = "photo/example.png";
                break;
            case "materialPhotoJPG":
                fileErrorMessage = checkFile(file, new String[]{".jpg"}, 10L * 1024 * 1024, "MB");
                fileTargetPath = "photo/example.jpg";
                break;
            case "audio":
                String fname = file.getOriginalFilename().toLowerCase();
                String suffix = StringUtils.getFilenameExtension(fname);
                fileErrorMessage = checkFile(file, new String[]{".wav", ".mp3", ".wma", ".m4a", ".aac"}, 10L * 1024 * 1024, "MB");
                fileTargetPath = "audio/example." + suffix;
                break;
            default:
                return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), "type 值必须是 (idcardMp4 视频格式授权书, " +
                    "idcardPdf 格式授权书PDF, materialVideo 形象定制素材, " +
                    "materialAudioZip 声音定制素材, materialPhotoPng 照片数智人定制素材PNG, materialPhotoJpg 照片数智人定制素材JPG, " +
                    "audio: 音频文件)");
        }
        if (fileErrorMessage != null) {
            return Result.error(ResultEnum.PARAMETER_EXCEPTION.getCode(), fileErrorMessage);
        }
        return Result.success(sdkTencentAiService.uploadTemp(file, type, fileTargetPath));
    }
    /**
     * 检查文件并返回错误信息字符串
     * fileErrorMessage = checkFile(file, ".mp4", 1L * 1024 * 1024 * 1024, "GB");
     */
    private String checkFile(MultipartFile file, String[] fileTypes, long fileMaxSize, String fileMaxSizeType) {
        String message = null;
        String fileName = file.getOriginalFilename().toLowerCase();

        // 判断文件类型
        boolean isValidType = false;
        for (String fileType : fileTypes) {
            if (fileName.endsWith(fileType.toLowerCase())) {
                isValidType = true;
                break;
            }
        }
        if (!isValidType) {
            message = "文件类型必须是以下类型之一: " + Arrays.toString(fileTypes);
        }
        // 判断文件大小
        if (file.getSize() > fileMaxSize) {
            if (fileMaxSizeType == "GB") {
                message = "文件不能大于 " + fileMaxSize / 1024 / 1024 / 1024 + " GB";
            }
            if (fileMaxSizeType == "MB") {
                message = "文件不能大于 " + fileMaxSize / 1024 / 1024 + " MB";
            }
        }
        return message;
    }



    /**
     *  第三方接口差异【形象与音色的接口返回有所不同】：
     *  [形象]：
     *  - 当没有自定义形象时，接口返回 {公共形象}
     *  - 当有自定义形象时，接口返回 {定制形象}
     *  [音色]：
     *  - 当没有自定义音色时，接口返回 {公共音色}
     *  - 当有自定义音色时，接口返回 {公共音色} + {自定义音色} (带有 expire_data 字段来区分)
     */

    /**
     * 流程差异【为什么要单独做 _custom 定制表？】
     * 因为现在的做法是：将一个我们公司的账号，做成可提供给客户的多个账号来使用
     *    但腾讯的接口，在定制完成后，并没有提供 anthor_code 或 virtualman_key 这些字段，而是需要主动去查询第三方列表
     *    而查询的结果，是返回当前公司账号中所有 {音色/形象}，这样就没办法区分不同客户所创建的不同 {音色/形象}
     *    所以需要在定制完成后，就录入能够区分用户所拥有 {音色/形象} 的字段 (user_id)
     *    (不能做在确认接口，因为从确认到生成这段时间有延时性)
     * 所以：
     *    当客户生成 "定制任务" 时，会创建一条 临时形象数据，带 { user_id, anchor_name } 参数
     *    当客户访问 "定制任务详情" 时，会判断任务是否完成 "CONFIRM"
     *      当任务完成时，将再次读取第三方形象接口，并通过遍历 anchor_name 这个唯一值，找到对应形象信息，并更新到对应的 临时形象数据
     */


    /**
     * 初始化公共主播 (第三方接口) (到本地数据库)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @PostMapping("/api/ai/ivh/asset/initPublicVirtualman")
    public Result initPublicVirtualman() {
        return Result.success(Map.of("total", aiivhAnchorService.insertAiivhAnchor(1, 100)));
    }

    /**
     * 初始化公共音色 (第三方接口) (到本地数据库) (SDK接口不能超过100个，目前有做循环遍历获得全部的数据)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @PostMapping("/api/ai/person/asset/initPublicTimbre")
    public Result initPublicTimbre() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        return Result.success(Map.of("total", aiPersonTimbreService.insertAiivhPublicTimbreBatch(1, 100)));
    }



    /**
     * 获取公共形象 (本地) (初始化后的结果查询)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getPublicVirtualman")
    public Result getPublicVirtualman(@Validated PageDTO pageDTO, @Validated AiivhAnchorPublicDTO aiivhAnchorPublicDTO) {
        return Result.success(aiivhAnchorService.queryAiivhPublicAnchorList(pageDTO.getPage_num(), pageDTO.getPage_size(), aiivhAnchorPublicDTO));
    }

    /**
     * 获取定制形象 (我的)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getMyCustomVirtualman")
    public Result getCustomVirtualman(@Validated PageDTO pageDTO, @Validated AiivhAnchorDTO aiivhAnchorDTO) {

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

        return Result.success(aiivhAnchorService.queryAiivhAnchorList(pageDTO.getPage_num(), pageDTO.getPage_size(), aiivhAnchorDTO));
    }



    /**
     * 获取公共音色 (本地) (初始化后的结果查询)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getPublicTimbre")
    public Result getPublicTimbre(@Validated PageDTO pageDTO, @Validated AiPersonTimbreDTO aiPersonTimbreDTO) {
        return Result.success(aiPersonTimbreService.queryAiivhPublicTimbreList(pageDTO.getPage_num(), pageDTO.getPage_size(), aiPersonTimbreDTO));
    }

    /**
     * 获取定制音色 (我的)
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getMyCutomTimbre")
    public Result getMyCutomTimbre(@Validated PageDTO pageDTO, @Validated AiPersonTimbreCustomDTO aiPersonTimbreCustomDTO) {

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

        return Result.success(aiPersonTimbreService.queryAiivhTimbreList(pageDTO.getPage_num(), pageDTO.getPage_size(), aiPersonTimbreCustomDTO));
    }

    /**
     * 音色情感分类
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getTimbreEmotionalCategory")
    public Result getTimbreEmotionalCategory(@Validated AiivhTimbreEmotionalCategoryDTO aiivhTimbreEmotionalCategoryDTO) {
        return Result.success(aiivhTimbreEmotionalCategoryMapper.queryAiivhTimbreEmotionalCategory(aiivhTimbreEmotionalCategoryDTO));
    }


    /**
     * 我的数智人资产一览
     */
    @PreAuthorize("@sr.hasPermission('33')")
    @GetMapping("/api/ai/ivh/asset/getMyOverview")
    public Result getTimbreEmotionalCategory() {
        return Result.success(aiivhAssetService.getMyOverview());
    }



}
