package com.sqx.modules.monitor;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ExecutorBuilder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sqx.common.utils.SpringContextUtils;
import com.sqx.ffmpeg.FFmpeg;
import com.sqx.modules.common.service.CommonInfoService;
import com.sqx.modules.message.controller.app.AppMessageController;
import com.sqx.modules.message.entity.MessageInfo;
import com.sqx.modules.message.entity.MessageInfoAudio;
import com.sqx.modules.message.service.MessageAudioService;
import com.sqx.modules.message.service.MessageService;
import com.sqx.ws.BaseInfoModel;
import com.sqx.ws.BaseModelEncoder;
import com.sqx.ws.HashMapEncoder;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

/**
 * WebSocket聊天实现
 */
@Component
@Scope("prototype")
@ServerEndpoint(value = "/monitorWebSocket/{businessId}", encoders = { HashMapEncoder.class, BaseModelEncoder.class })
public class MonitorWebSocket {
    private static final Logger log = LoggerFactory.getLogger(MonitorWebSocket.class);

    //通过类似GET请求方式传递参数的方法（服务端采用第二种方法"WebSocketHandler"实现）
    //websocket = new WebSocket("ws://127.0.0.1:18080/testWebsocket?id=23&name=Lebron");
    /**
     * 在线人数
     */
    public static int onlineNumber = 0;
    /**
     * 以用户的id为key，WebSocket为对象保存起来
     */
    private static final Map<String, MonitorWebSocket> clients = new ConcurrentHashMap<>();

    private static final Map<String, ExecutorService> executorServiceMap = Maps.newHashMap();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户id
     */
    private String businessId;

    //此处不需要
//    //这里使用静态，让 service 属于类
//    private static ChatsContentService chatContentService;
//
//    //注入的时候，给类的 service 注入
//    @Autowired
//    public void setWxChatContentService(ChatsContentService chatContentService) {
//        MapWebSocket.chatContentService = chatContentService;
//    }

    private final CommonInfoService commonRepository = SpringContextUtils.getBean(CommonInfoService.class);

    private final FFmpeg fFmpeg = SpringContextUtils.getBean(FFmpeg.class);

    private final AppMessageController appMessageController = SpringContextUtils.getBean(AppMessageController.class);

    private final MessageService messageService = SpringContextUtils.getBean(MessageService.class);

    private final MessageAudioService messageAudioService = SpringContextUtils.getBean(MessageAudioService.class);


    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("businessId") String businessId, Session session) {
        onlineNumber++;
        log.info("客户端连接Id: {}", businessId);
        this.businessId = businessId;
        this.session = session;
        log.info("有新连接加入！ 当前在线设备: {}", onlineNumber);
        try {
            //把自己的信息加入到map当中去
            clients.remove(businessId);
            clients.put(businessId, this);
            /*sendMessageTo("恭喜你连接成功！",wxUserId);*/
        } catch (Exception e) {
            log.info("{} 连接发生了错误", businessId);
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.info("服务端发生了错误: {}", error.getMessage());
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        onlineNumber--;
        //webSockets.remove(this);
        clients.remove(businessId);
        log.info("有连接关闭！ 当前在线设备: {}", onlineNumber);
    }

    /**
     * 接收客户端的消息，并把消息发送给所有连接的会话
     */
    @OnMessage
    public void onMessage(String message) {
        try {
            log.info("客户端发来的消息：{}", message);
            //解析聊天内容
            JSONObject jsonObject = JSON.parseObject(message);
            String sendType = jsonObject.getString("sendType");
            String userId = jsonObject.getString("userId");
            String date = jsonObject.getString("date");
            String businessId = jsonObject.getString("businessId");

            String http = commonRepository.findOne(19).getValue();
            String[] split = http.split("://");
            // 上传文件路径
            String baseAudioUploadPath = "/www/wwwroot/"+split[1]+"/file/uploadPath";

            String baseFilePath = baseAudioUploadPath + "/" + "audio" + "/" + userId + "/" + date + "/" + businessId + "/";
            switch (sendType){
                case "creatFile": {
                    log.info("creatFile开始！");
                    ExecutorService executor = ExecutorBuilder.create()//创建单线程线程池，让其慢慢执行
                            .setCorePoolSize(1)//初始线程数为1
                            .setMaxPoolSize(1)//最大线程为1
                            .setKeepAliveTime(0)//线程存活时间
                            .build();
                    executorServiceMap.put(businessId,executor);
                    Callable<Boolean> call = () -> {
                        log.info("消息保存成功！");
                        //String filePath = baseFilePath + "all.mp3";
                        //FileUtil.touch(filePath);
                        //String url = StringUtils.replace(filePath,"/www/wwwroot/"+split[1],http);
                        String userName = jsonObject.getString("userName");
                        String typeId = jsonObject.getString("typeId");
                        String typeName = jsonObject.getString("typeName");

                        //
                        MessageInfo messageInfo = new MessageInfo();
                        //messageInfo.setImage(url);
                        messageInfo.setState("9");
                        messageInfo.setTypeId(typeId);
                        messageInfo.setTypeName(typeName);
                        messageInfo.setUserId(userId);
                        messageInfo.setUserName(userName);
                        messageInfo.setBatch(businessId);
                        appMessageController.insertMessage(messageInfo);
                        log.info("消息保存成功！");
                        return true;
                    };
                    executor.submit(call);



                    //01，，不执行了
                    //String fileName = jsonObject.getString("fileName");
                    //String basePath = FileUploadUtils.getDefaultBaseDir();
                    //log.info("基础地址：", basePath);

                    //https://wx.sjajk.com/file/uploadPath\audios\333\2023-12-08\\3454e2c9-fc9f-4134-8c18-f50855e99ab2\
//                    baseAudioUploadPath.replace(,);
//                    String url = http +fileName;


                    //创建地址
                    //String userId = jsonObject.getString("userId");

                    sendMessageObject(session, BaseInfoModel.success("creatFile","加入队列成功！"));//回执，告知已记录
                    break;
                }
//                case "startUpload": {
//                    //01 开始上传
//                    //创建基本文件夹（临时存这里）
//                    FileUtil.mkdir("D:\\opt\\upload2" + businessId + File.separator + userId + File.separator);
//                    //创建文件列表index文件
//                    //index = `
//                    // 文件原名 2023年11月20日09:25:113
//                    // 0001:xmsxmsdasdmsa
//                    // 0002:adadasdadasdd
//                    // 0003:adadasdadasd1
//                    // ...
//                    // `
//                    sendMessageObject(session, BaseInfoModel.success("startUpload","ok"));//准备工作做完可以开始上传了
//                    //前端接到ok，开始02上传文件
//                    break;
//                }
                case "uploadFileData": {
                    //02~文件上传(N次上传)
                    //将其扔进线程池里。
                    ExecutorService executor = executorServiceMap.get(businessId);
                    Callable<Boolean> call = () -> {
                        //写入磁盘
                        String currentUploadFragment = jsonObject.getString("currentUploadFragment");//序号
                        String fileData = jsonObject.getString("fileData");
                        if(StringUtils.isNotBlank(fileData)){
                            byte[] fileByte = Base64.decode(fileData);//传过来的是base64，解析成字节码
                            //创建文件
                            String filePath = baseFilePath + currentUploadFragment + ".mp3";
                            FileUtil.touch(filePath);
                            FileUtil.writeBytes(fileByte,filePath);//按字节码甩今文件里
                            log.info("创建文件【{}】成功！",filePath);
                        }
                        //分批合并
                        int currentUploadFragmentI = Integer.parseInt(currentUploadFragment);
                        int i = currentUploadFragmentI-1;
                        int cycle = i % 15;
                        int mergeFileName = (i / 15) + 1;
                        if(cycle == 0){
                            log.info("分段创建：第{}段，创建的文件名为：{}，合并后文件名为：{}，", cycle,currentUploadFragmentI, mergeFileName);
                            //第一个，创建文件并保存
                            //第一个文件，复制到all.mp3
                            String filePath = baseFilePath + currentUploadFragment + ".mp3";
                            String allFilePath = baseFilePath + "merge" + mergeFileName +".mp3";
                            String url = StringUtils.replace(allFilePath,"/www/wwwroot/"+split[1],http);
                            FileUtil.copy(filePath, allFilePath, true);//拷贝
                            //删除头一个文件
                            FileUtil.del(filePath);

                            //保存进子表里
                            MessageInfoAudio messageInfoAudio = new MessageInfoAudio();
                            messageInfoAudio.setBatch(businessId);
                            messageInfoAudio.setAuditContent(url);
                            messageInfoAudio.setSort(mergeFileName);
                            messageAudioService.save(messageInfoAudio);

                            sendMessageObject(session, BaseInfoModel.success("uploadFileData","01创建:ok"));//上传文件的md5以代表上传成功
                            return true;
                        }else{
                            log.info("分段合并：第{}段，创建的文件名为：{}，合并后文件名为：{}，", cycle,currentUploadFragmentI, mergeFileName);
                            //中间的正常合并
                            //合并all
                            String filePath = baseFilePath + currentUploadFragment + ".mp3";
                            String allFilePath = baseFilePath + "merge" + mergeFileName +".mp3";
                            String outallFilePath = baseFilePath + "merge" + mergeFileName +"_temp.mp3";
                            //判断是不是有ALL，没有就做一个
                            if(!FileUtil.exist(allFilePath)){
                                //复制一个当前音频，跳到下一个周期
                                FileUtil.copy(filePath,allFilePath,true);
                                FileUtil.del(filePath);
                                return true;
                            }
                            fFmpeg.montageAudio(allFilePath, filePath, outallFilePath);

                            //合并完了之后
                            //改名，删除合并完成的文件
                            FileUtil.copy(outallFilePath,allFilePath,true);
                            FileUtil.del(filePath);
                            FileUtil.del(outallFilePath);
                        }

                        //合并
//                        if(StringUtils.equals(currentUploadFragment,"1")){
//                            //第一个文件，复制到all.mp3
//                            String filePath = baseFilePath + currentUploadFragment + ".mp3";
//                            String allFilePath = baseFilePath + "all.mp3";
//                            FileUtil.copy(filePath, allFilePath, true);//拷贝
//                            //删除头一个文件
//                            FileUtil.del(filePath);
//                            sendMessageObject(session, BaseInfoModel.success("uploadFileData","01创建:ok"));//上传文件的md5以代表上传成功
//                            return true;
//                        }else{
//                            //合并all
//                            String filePath = baseFilePath + currentUploadFragment + ".mp3";
//                            String allFilePath = baseFilePath + "all.mp3";
//                            String outallFilePath = baseFilePath + "all_temp.mp3";
//                            //判断是不是有ALL，没有就做一个
//                            if(!FileUtil.exist(allFilePath)){
//                                //复制一个当前音频，跳到下一个周期
//                                FileUtil.copy(filePath,allFilePath,true);
//                                FileUtil.del(filePath);
//                                return true;
//                            }
//                            fFmpeg.montageAudio(allFilePath, filePath, outallFilePath);
//
//                            //合并完了之后
//                            //改名，删除合并完成的文件
//                            FileUtil.copy(outallFilePath,allFilePath,true);
//                            FileUtil.del(filePath);
//                            FileUtil.del(outallFilePath);
//                        }
                      return true;
                    };
                    executor.submit(call);
                    sendMessageObject(session, BaseInfoModel.success("uploadFileData","ok"));//上传文件的md5以代表上传成功
                    break;
                }
                case "endUpload": {
                    ExecutorService executor = executorServiceMap.get(businessId);
                    Callable<Boolean> call = () -> {
                        log.info("执行完成！，队列的最末尾");
                        String filePath = baseFilePath + "all.mp3";
                        FileUtil.touch(filePath);
                        String url = StringUtils.replace(filePath,"/www/wwwroot/"+split[1],http);
                        //执行合并
                        //查询出已经合并后的文件列表
                        QueryWrapper<MessageInfoAudio> qw = new QueryWrapper<>();
                        qw.eq("batch",businessId);
                        List<MessageInfoAudio> audioList = messageAudioService.list(qw);
                        List<String> mergeList = Lists.newArrayList();
                        for (MessageInfoAudio audio: audioList){
                            //转换成物理地址
                            String fileUrl = audio.getAuditContent();
                            String path = StringUtils.replace(fileUrl,http, "/www/wwwroot/"+split[1]);
                            mergeList.add(path);
                        }
                        fFmpeg.mergeAllAudio(mergeList,filePath);
                        UpdateWrapper<MessageInfo> uw = new UpdateWrapper<>();
                        uw.set("image",url);
                        uw.eq("batch",businessId);
                        messageService.update(uw);
                        return true;
                    };
                    executor.submit(call);
                    //03手动结束上传
                    //暂时还不知道干点啥，，空着吧
                    //立刻合并剩下的内容
                    sendMessageObject(session, BaseInfoModel.success("endUpload","ok"));//准备工作做完可以开始上传了
                    break;
                }
                default: {
                    log.info("意外的解析数据：{}", message);
                }
            }
        } catch (Exception e) {
            log.info("发生了错误了",e);
        }
    }

    /**
     * 发送消息，实践表明，每次浏览器刷新，session会发生变化。
     * @param session
     * @param message
     */
    public static void sendMessage(Session session, String message) throws IOException {
        try {
//          session.getBasicRemote().sendText(String.format("%s (From Server，Session ID=%s)",message,session.getId()));
            session.getBasicRemote().sendText(message);
            log.info("ws发生回执: {}",message);
        } catch (IOException e) {
            log.error("发送消息出错：{}", e.getMessage());
            log.error(e.getMessage(),e);
        }
    }

    public static void sendMessageObject(Session session, Object message) throws IOException {
        try {
            session.getBasicRemote().sendObject(message);
            log.info("ws发生回执: {}",message);
        } catch (IOException | EncodeException e) {
            log.error("发送消息出错：{}", e.getMessage());
            log.error(e.getMessage(),e);
        }
    }


    public static synchronized int getOnlineCount() {
        return onlineNumber;
    }

    public static void main(String[] args) {
        for (int currentUploadFragment = 1; currentUploadFragment <= 1000; currentUploadFragment++) {
            int i = currentUploadFragment-1;
            int cycle = i % 15;
            int mergeFileName = (i / 15) + 1;
            if(cycle == 0){
                System.out.println("创建文件:" + i + ",合并后文件名为:" + mergeFileName + ",合并生命周期:" + cycle);
            }else{
                System.out.println("合并文件:" + i + ",合并后文件名为:" + mergeFileName + ",合并生命周期:" + cycle);
            }
        }
    }


}
