package com.fdkankan.ucenter.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fdkankan.common.constant.SceneConstant;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.common.util.SecurityUtil;
import com.fdkankan.fyun.face.FYunFileServiceInterface;
import com.fdkankan.redis.constant.RedisKey;
import com.fdkankan.redis.util.RedisLockUtil;
import com.fdkankan.redis.util.RedisUtil;
import com.fdkankan.ucenter.common.DownloadStatusEnum;
import com.fdkankan.ucenter.common.RedisKeyUtil;
import com.fdkankan.ucenter.common.constants.ResultCodeMsg;
import com.fdkankan.ucenter.constant.CameraConstant;
import com.fdkankan.ucenter.constant.LoginConstant;
import com.fdkankan.ucenter.entity.*;
import com.fdkankan.ucenter.httpClient.service.LaserService;
import com.fdkankan.ucenter.httpClient.vo.SSDownSceneVo;
import com.fdkankan.ucenter.httpClient.vo.SceneStatusInfoDTO;
import com.fdkankan.ucenter.service.*;
import com.fdkankan.ucenter.vo.response.DownVo;
import com.fdkankan.ucenter.vo.response.DownloadProcessVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class DownService implements IDownService {

    @Autowired
    ISceneProService sceneProService;
    @Autowired
    ISceneProEditService sceneProEditService;
    @Autowired
    ISceneDownloadLogService sceneDownloadLogService;
    @Autowired
    IScenePlusService scenePlusService;
    @Autowired
    IScenePlusExtService scenePlusExtService;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    RedisLockUtil redisLockUtil;
    @Autowired
    ISceneEditInfoService sceneEditInfoService;
    @Autowired
    IUserService userService;
    @Autowired
    LaserService laserService;
    @Autowired
    ICameraTypeService cameraTypeService;
    @Autowired
    FYunFileServiceInterface fYunFileServiceInterface;

    @Override
    public DownVo checkDownLoad(String sceneNum,Integer isObj) {
        if(StringUtils.isEmpty(sceneNum)){
            throw new BusinessException(LoginConstant.FAILURE_CODE_3001, LoginConstant.FAILURE_MSG_3001);
        }
        ScenePro scenePro = sceneProService.getByNum(sceneNum);
        ScenePlus plus = scenePlusService.getByNum(sceneNum);
        if(scenePro == null && plus == null){
            throw new BusinessException(SceneConstant.FAILURE_CODE_5005, SceneConstant.FAILURE_MSG_5005);
        }
        isObj = isObj == null ?1 :isObj;
        log.info("checkDownLoad--,isObj:{}",isObj);
        if(isObj !=1){ //深时场景
            return SSCheckDownload(sceneNum);
        }
        SceneDownloadLog sceneDownloadLog;
        int isUp = 0;
        if(scenePro == null){
            isUp = 1;
        }
        Integer sceneVersion = getSceneVersion(scenePro, plus);

        sceneDownloadLog = sceneDownloadLogService.getByStatusAndNum(sceneNum,0,isUp,sceneVersion);
        DownVo downVo = new DownVo();
        if(sceneDownloadLog != null ){
            downVo.setDownloadStatus(1);
            return downVo;
        }
        sceneDownloadLog = sceneDownloadLogService.getByStatusAndNum(sceneNum,1,isUp,sceneVersion);
        //3下载过，并且没有修改过
        if(sceneDownloadLog != null ){
            downVo.setDownloadStatus(3);
            downVo.setDownloadUrl(sceneDownloadLog.getDownloadUrl());
            return downVo;
        }
        return downVo;
    }


    private Integer getSceneVersion(ScenePro scenePro,ScenePlus scenePlus){
        Integer version = 0;
        if(scenePro != null){
            SceneProEdit proEdit = sceneProEditService.getByProId(scenePro.getId());
            if(proEdit == null){
                throw new BusinessException(SceneConstant.FAILURE_CODE_5005, SceneConstant.FAILURE_MSG_5005);
            }
            version = proEdit.getVersion();
        }
        if(scenePro == null && scenePlus !=null){
            String redisKey = String.format(RedisKeyUtil.SCENE_VERSION,scenePlus.getNum());
            if(!redisUtil.hasKey(redisKey) || StringUtils.isBlank(redisUtil.get(redisKey))){
                SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(scenePlus.getId());
                if(sceneEditInfo == null){
                    throw new BusinessException(SceneConstant.FAILURE_CODE_5005, SceneConstant.FAILURE_MSG_5005);
                }
                version = sceneEditInfo.getVersion();
            }else {
                String redisObj = redisUtil.get(redisKey);
                JSONObject obj = JSONObject.parseObject(redisObj);
                version = obj.getInteger("version");
            }
        }
        return version;
    }

    @Override
    public DownVo down(String sceneNum,String userName,Integer isObj) {
        if(StringUtils.isEmpty(sceneNum) || StringUtils.isEmpty(userName)){
            throw new BusinessException(LoginConstant.FAILURE_CODE_3001, LoginConstant.FAILURE_MSG_3001);
        }
        ScenePro scenePro = sceneProService.getByNum(sceneNum);
        ScenePlus plus = scenePlusService.getByNum(sceneNum);
        if(scenePro == null && plus == null){
            throw new BusinessException(SceneConstant.FAILURE_CODE_5005, SceneConstant.FAILURE_MSG_5005);
        }

        isObj = isObj == null ?1 :isObj;
        DownVo downVo = new DownVo();
        User user = userService.getByUserName(userName);
        if(user == null){
            downVo.setDownloadStatus(-1);
            return downVo;
        }
        Integer downLoadTotal = 0;
        Integer downLoadNum = 0;
        downLoadTotal = user.getDownloadNumTotal();
        downLoadNum = user.getDownloadNum();
        if(downLoadTotal <= downLoadNum ){
            downVo.setDownloadStatus(-1);
            return downVo;
        }
        log.info("down-:isObj:{}",isObj);
        if(isObj !=1){ //深时场景
            return SSDownload(sceneNum,user);
        }

        Integer sceneVersion = getSceneVersion(scenePro, plus);

        Map<String,String> params = new HashMap<>(2);
        params.put("type","local");
        params.put("sceneNum",sceneNum);
        String progressKey = String.format(RedisKey.PREFIX_DOWNLOAD_PROGRESS, sceneNum);
        String downloadTaskKey = RedisKey.DOWNLOAD_TASK;
        String sysVersion = "v3";
        if(scenePro == null){
            params.put("num",sceneNum);
            progressKey = String.format(RedisKey.PREFIX_DOWNLOAD_PROGRESS_V4, sceneNum);
            downloadTaskKey = RedisKey.SCENE_DOWNLOADS_TASK_V4;
            sysVersion = "v4";
        }
        //先删除之前的下载进度
        redisUtil.del(progressKey);
        //入队
        redisUtil.lRightPush(downloadTaskKey, JSONObject.toJSONString(params));

        //修改用户的下载次数
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getId,user.getId());
        wrapper.setSql("download_num = download_num + " + 1);
        userService.update(wrapper);
        SceneDownloadLog sceneDownloadLogEntity = new SceneDownloadLog();
        sceneDownloadLogEntity.setUserId(user.getId());
        sceneDownloadLogEntity.setSceneNum(sceneNum);
        sceneDownloadLogEntity.setSceneVersion(sceneVersion);
        sceneDownloadLogEntity.setStatus(0);
        sceneDownloadLogEntity.setSysVersion(sysVersion);
        sceneDownloadLogService.save(sceneDownloadLogEntity);

        downVo.setDownloadStatus(1);
        return downVo;
    }

    @Override
    public DownloadProcessVo downloadProcess(Long userId, String sceneNum,Integer isObj) {
        if (StringUtils.isEmpty(sceneNum)) {
            throw new BusinessException(LoginConstant.FAILURE_CODE_3001, LoginConstant.FAILURE_MSG_3001);
        }
        ScenePro scenePro = sceneProService.getByNum(sceneNum);
        ScenePlus plus = scenePlusService.getByNum(sceneNum);
        if(scenePro == null && plus == null){
            throw new BusinessException(SceneConstant.FAILURE_CODE_5005, SceneConstant.FAILURE_MSG_5005);
        }

        isObj = isObj == null ? 1 :isObj;
        log.info("downloadProcess--,isObj:{}",isObj);
        if( isObj !=1){ //深时场景
            return SSDownloadProcess(sceneNum);
        }
        String redisKey = RedisKey.PREFIX_DOWNLOAD_PROGRESS;
        if(scenePro == null){
            redisKey = RedisKey.PREFIX_DOWNLOAD_PROGRESS_V4;
        }
        // 获取下载进度
        String result = redisUtil.get(String.format(redisKey,sceneNum));
        if(StringUtils.isEmpty(result)){
            return new DownloadProcessVo();
        }
        int isUp = 0;
        if(scenePro == null){
            isUp = 1;
        }
        Integer sceneVersion = getSceneVersion(scenePro, plus);

        SceneDownloadLog sceneDownloadLog = sceneDownloadLogService.getByStatusAndNum(sceneNum,0,isUp,sceneVersion);

        DownloadProcessVo downloadProcessVo = JSONObject.parseObject(result, DownloadProcessVo.class);
        if(sceneDownloadLog != null){
            switch (downloadProcessVo.getStatus()) {
                case DownloadStatusEnum.DOWNLOAD_SUCCESS_CODE:
                    String url = downloadProcessVo.getUrl();
                    if (!StringUtils.isEmpty(url)) {
                        sceneDownloadLog.setDownloadUrl(url);
                        sceneDownloadLog.setStatus(1);
                        break;
                    }
                case DownloadStatusEnum.DOWNLOAD_FAILED_CODE:
                    sceneDownloadLog.setStatus(-1);
                    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
                    wrapper.eq(User::getId,userId);
                    wrapper.setSql("download_num = download_num - " + 1);
                    userService.update(wrapper);
                    break;
            }
            sceneDownloadLogService.updateById(sceneDownloadLog);
        }

        return downloadProcessVo;
    }


    /**
     * status ：离线包状态是否需要重新生成  0 未生成：1 不需要  2需要   3 生成中
     */
    private DownVo SSCheckDownload(String sceneNum) {
        DownVo downVo = new DownVo();
        SSDownSceneVo vo = laserService.downOfflineSceneStatus(sceneNum);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        downVo.setDownloadStatus(0);
        if(vo.getStatus() == 1){
            downVo.setDownloadStatus(3);
            downVo.setDownloadUrl(vo.getUrl());
        }
        if(vo.getStatus() == 2){
            downVo.setDownloadStatus(2);
        }
        if(vo.getStatus() == 3){
            downVo.setDownloadStatus(1);
        }
        return downVo;
    }

    /**
     * downloadStatus -1下载失败  1下载成功
     */
    private DownVo SSDownload(String sceneNum,User user) {
        DownVo downVo = new DownVo();
        //status ：0:正在生成 1，初次生成  2，已经生成直接下载 3，重新生成
        SSDownSceneVo vo = laserService.downOfflineScene(sceneNum);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(User::getId,user.getId());
        wrapper.setSql("download_num = download_num + " + 1);
        userService.update(wrapper);

        SceneDownloadLog sceneDownloadLogEntity = new SceneDownloadLog();
        sceneDownloadLogEntity.setUserId(user.getId());
        sceneDownloadLogEntity.setSceneNum(sceneNum);
        sceneDownloadLogEntity.setSceneVersion(0);
        sceneDownloadLogEntity.setStatus(0);
        sceneDownloadLogEntity.setSysVersion("ss");
        sceneDownloadLogService.save(sceneDownloadLogEntity);
        downVo.setDownloadStatus(1);
        return downVo;
    }

    public static HashMap<String,Integer> ssNumProcessNumMap = new HashMap<>();

    private DownloadProcessVo SSDownloadProcess(String sceneNum) {
        DownloadProcessVo downVo = new DownloadProcessVo();
        SSDownSceneVo vo = laserService.downOfflineSceneStatus(sceneNum);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        downVo.setStatus(1003);

        if(vo.getStatus() == 0 || vo.getStatus() == 2 || vo.getStatus() == 3){    //下载中
            ssNumProcessNumMap.merge(sceneNum, 1, Integer::sum);
            Integer percent = ssNumProcessNumMap.get(sceneNum);
            percent = percent /2;
            if(percent >50){
                percent = 50;
            }
            downVo.setStatus(1001);
            downVo.setPercent(percent);
        }
        if(vo.getStatus() == 1){    //下载完成
            ssNumProcessNumMap.remove(sceneNum);
            downVo.setPercent(100);
            downVo.setUrl(vo.getUrl());
            downVo.setStatus(1002);

            LambdaUpdateWrapper<SceneDownloadLog> wrapper = new LambdaUpdateWrapper<>();
            wrapper.eq(SceneDownloadLog::getSceneNum,sceneNum);
            wrapper.eq(SceneDownloadLog::getStatus,0);
            wrapper.set(SceneDownloadLog::getDownloadUrl,vo.getUrl());
            wrapper.set(SceneDownloadLog::getStatus,1);
            sceneDownloadLogService.update(wrapper);
        }
        return downVo;
    }


    @Autowired
    ICameraDetailService cameraDetailService;
    @Autowired
    ISceneColdStorageService sceneColdStorageService;

    @Override
    public DownVo checkDownLoadE57(String num,String splitType,String userName) {
        checkPerm(num,userName);

        DownVo downVo = new DownVo();
        SSDownSceneVo vo = laserService.downE57Status(num,splitType);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        downVo.setDownloadStatus(0);
        if(vo.getStatus() == 1){
            downVo.setDownloadStatus(3);
            downVo.setDownloadUrl(vo.getUrl());
        }
        if(vo.getStatus() == 2){
            downVo.setDownloadStatus(2);
        }
        if(vo.getStatus() == 3){
            downVo.setDownloadStatus(1);
        }
        if(vo.getStatus() == -1){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400016, ResultCodeMsg.FAILURE_MSG_400016);
        }
        return downVo;
    }

    private void checkPerm(String num, String userName) {
        User user = userService.getByUserName(userName);
        if(user == null){
            throw new BusinessException(LoginConstant.FAILURE_CODE_3015,LoginConstant.FAILURE_MSG_3015);
        }
        ScenePlus scenePlus = scenePlusService.getByNum(num);
        if(scenePlus == null ){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400002, ResultCodeMsg.FAILURE_MSG_400002);
        }
        if(scenePlus.getSceneSource() != 4 && scenePlus.getSceneSource() != 5){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400011, ResultCodeMsg.FAILURE_MSG_400011);
        }
        if(scenePlus.getUserId()!= null && !scenePlus.getUserId().equals(user.getId())){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400012, ResultCodeMsg.FAILURE_MSG_400012);
        }
        if(scenePlus.getCameraId() == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400010, ResultCodeMsg.FAILURE_MSG_400010);
        }
        CameraDetail cameraDetail = cameraDetailService.getByCameraId(scenePlus.getCameraId());
        if(cameraDetail == null){
            throw new BusinessException(CameraConstant.FAILURE_CODE_6029,CameraConstant.FAILURE_MSG_6029);
        }
        if(!cameraDetail.getUserId().equals(user.getId())){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400012, ResultCodeMsg.FAILURE_MSG_400012);
        }
        SceneColdStorage sceneColdStorage = sceneColdStorageService.getByNum(num);
        if(sceneColdStorage != null && sceneColdStorage.getState() == 1){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400013, ResultCodeMsg.FAILURE_MSG_400013);
        }
        ScenePlusExt scenePlusExt = scenePlusExtService.getByPlusId(scenePlus.getId());
        String dataSource = scenePlusExt.getDataSource();
        if(!fYunFileServiceInterface.fileExist(dataSource.replace("/mnt/data","home")+"/data.fdage")){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400014, ResultCodeMsg.FAILURE_MSG_400014);
        }
        SceneStatusInfoDTO dto = laserService.laserSceneInfo(num);
        if(dto == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        if(dto.getStatus()!=2 ){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400015, ResultCodeMsg.FAILURE_MSG_400015);

        }

    }

    @Override
    public synchronized DownVo downE57(String num,String splitType,String userName) {
        DownVo downVo = new DownVo();
        downVo.setDownloadStatus(1);
        if(redisUtil.hasKey(String.format(RedisKeyUtil.downE57Key,num))){
            return downVo;
        }
        checkPerm(num,userName);
        //status ：0:正在生成 1，初次生成  2，已经生成直接下载 3，重新生成
        SSDownSceneVo vo = laserService.downE57(num,splitType);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        redisUtil.set(String.format(RedisKeyUtil.downE57Key,num),"1",60);
        return downVo;
    }

    public static HashMap<String,Integer> ssNumProcessNumE57Map = new HashMap<>();


    @Override
    public DownloadProcessVo downloadProcessE57(String num,String splitType,String userName) {
        //checkPerm(num,userName);
        DownloadProcessVo downVo = new DownloadProcessVo();
        SSDownSceneVo vo = laserService.downE57Status(num,splitType);
        if(vo == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400003, ResultCodeMsg.FAILURE_MSG_400003);
        }
        if(vo.getStatus() ==-1){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400016, ResultCodeMsg.FAILURE_MSG_400016);
        }
        downVo.setStatus(1003);
        if(vo.getStatus() == 0 || vo.getStatus() == 2 || vo.getStatus() == 3){    //下载中
            ssNumProcessNumE57Map.merge(num, 1, Integer::sum);
            Integer percent = ssNumProcessNumE57Map.get(num);
            percent = percent /2;
            if(percent >50){
                percent = 50;
            }
            downVo.setStatus(1001);
            downVo.setPercent(percent);
        }
        if(vo.getStatus() == 1 ){    //下载完成
            ssNumProcessNumE57Map.remove(num);
            downVo.setPercent(100);
            downVo.setUrl(vo.getUrl());
            downVo.setStatus(1002);
        }
        return downVo;
    }

}
