package com.fdkankan.ucenter.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdkankan.common.constant.CommonStatus;
import com.fdkankan.common.constant.ErrorCode;
import com.fdkankan.common.constant.SceneVersionType;
import com.fdkankan.common.exception.BusinessException;
import com.fdkankan.fyun.face.FYunFileServiceInterface;
import com.fdkankan.model.utils.CreateObjUtil;
import com.fdkankan.ucenter.common.PageInfo;
import com.fdkankan.ucenter.common.RedisKeyUtil;
import com.fdkankan.ucenter.common.Result;
import com.fdkankan.ucenter.common.SceneSourceUtil;
import com.fdkankan.ucenter.common.constants.ConstantFilePath;
import com.fdkankan.ucenter.common.constants.ResultCodeMsg;
import com.fdkankan.ucenter.common.constants.UploadFilePath;
import com.fdkankan.redis.util.RedisUtil;
import com.fdkankan.ucenter.entity.*;
import com.fdkankan.ucenter.httpClient.service.LaserService;
import com.fdkankan.ucenter.mapper.IScenePlusMapper;
import com.fdkankan.ucenter.service.*;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

import com.fdkankan.ucenter.util.RoamingPointUtil;
import com.fdkankan.ucenter.util.SceneResourcePath;
import com.fdkankan.ucenter.vo.RelicsSceneInitQueueDTO;
import com.fdkankan.ucenter.vo.request.LaserSceneParam;
import com.fdkankan.ucenter.vo.request.RequestScene;
import com.fdkankan.ucenter.vo.response.SceneNumVo;
import com.fdkankan.ucenter.vo.response.SceneVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 场景主表 服务实现类
 * </p>
 *
 * @author 
 * @since 2022-07-04
 */
@Service
@Slf4j
public class ScenePlusServiceImpl extends ServiceImpl<IScenePlusMapper, ScenePlus> implements IScenePlusService {

    @Value("${fyun.host}")
    private String fyunHost;

    @Autowired
    private IScenePlusExtService scenePlusExtService;
    @Autowired
    public SceneCommonService sceneCommonService;
    @Autowired
    private ISceneEditInfoService sceneEditInfoService;
    @Autowired
    private ISceneEditInfoExtService sceneEditInfoExtService;
    @Autowired
    private ISceneEditControlsService sceneEditControlsService;
    @Autowired
    private ICameraDetailService cameraDetailService;
    @Autowired
    private ISurveillanceService surveillanceService;
    @Autowired
    private ISceneProService sceneProService;
    @Autowired
    FYunFileServiceInterface fYunFileServiceInterface;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    LaserService laserService;
    @Autowired
    ISceneCopyLogService sceneCopyLogService;
    @Autowired
    private IUserService userService;
    @Autowired
    ICameraTypeService cameraTypeService;


    @Override
    public Long getCountByUserId(Long userId, List<?> sceneSourceList) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScenePlus::getUserId, userId);
        wrapper.in(ScenePlus::getSceneSource, sceneSourceList);
        return this.count(wrapper);
    }

    @Override
    public Long getCountByUserId(Long userId, Integer cameraType) {
        return this.getBaseMapper().getCountByUserId(userId,cameraType);
    }


    @Override
    public List<ScenePlus> getListByCameraId(Long cameraId) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScenePlus::getCameraId,cameraId);
        return this.list(wrapper);
    }

    @Override
    public void bindOrUnCamera(List<Long> cameraIds, Long userId) {
        if(cameraIds.size() >0){
            LambdaUpdateWrapper<ScenePlus> wrapper = new LambdaUpdateWrapper<>();
            wrapper.set(ScenePlus::getUserId,userId)
                    .in(ScenePlus::getCameraId,cameraIds);
            this.update(wrapper);
        }
    }

    @Override
    public List<ScenePlus> getListByCameraIds(List<Long> cameraIds) {
        if(cameraIds.size() >0){
            LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(ScenePlus::getCameraId,cameraIds);
            return this.list(wrapper);
        }
        return new ArrayList<>();
    }

    @Override
    public List<ScenePlus> getListByNums(List<String> numList) {
        if(numList.isEmpty()){
            return new ArrayList<>();
        }
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ScenePlus::getNum,numList);
        return this.list(wrapper);
    }

    @Override
    public HashMap<Long, ScenePlus> getMapByIds(List<Long> sceneIds) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ScenePlus::getId,sceneIds);
        List<ScenePlus> list = this.list(wrapper);
        HashMap<Long,ScenePlus> map = new HashMap<>();
        list.forEach(entity -> map.put(entity.getId(),entity));
        return map;
    }

    @Override
    public ScenePlus getByNum(String sceneNum) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScenePlus::getNum,sceneNum);
        List<ScenePlus> list = this.list(wrapper);
        if(list!=null && list.size() >0){
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<ScenePlus> getByIds(List<Long> plusIds) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(ScenePlus::getId,plusIds);
        return  this.list(wrapper);
    }

    @Override
    public Long copyV4Scene(ScenePlus scenePlus,String newNum ,CameraDetail cameraDetail,CameraType cameraType)  {
        String oldNum = scenePlus.getNum();

        Long plusId = scenePlus.getId();

        ScenePlusExt plusExt = scenePlusExtService.getByPlusId(plusId);
        if(plusExt == null){
            throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400002, ResultCodeMsg.FAILURE_MSG_400002);
        }
        scenePlus.setNum(newNum);
        scenePlus.setTitle(scenePlus.getTitle() +"(copy)");
        scenePlus.setSceneStatus(0);
        scenePlus.setId(null);
        this.save(scenePlus);

        ExecutorService executor = ThreadUtil.newSingleExecutor();
        CompletableFuture.runAsync(() -> {
            String oldDataSource = plusExt.getDataSource();

            String newDataSource = sceneCommonService.getNewDataSource(oldDataSource);

            log.info("sceneCopy-V4-oldNum:{},oldDataSource:{},newNum:{},newDataSource:{}",
                    oldNum,oldDataSource,newNum,newDataSource);

            String newVideos = plusExt.getVideos();
            if(StrUtil.isNotEmpty(newVideos)){
                newVideos = plusExt.getVideos().replaceAll("/data/data" + oldNum, "/scene_view_data/" + newNum + "/data").replaceAll(oldNum, newNum);
            }
            plusExt.setId(null);
            plusExt.setPlusId(scenePlus.getId());
            plusExt.setDataSource(newDataSource);
            plusExt.setWebSite(plusExt.getWebSite().replace(oldNum, newNum));
            plusExt.setThumb(plusExt.getThumb().replace(oldNum, newNum));
            plusExt.setVideos(newVideos);
            plusExt.setViewCount(0);
            scenePlusExtService.save(plusExt);

            SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(plusId);
            Long sceneEditInfoId = sceneEditInfo.getId();

            sceneEditInfo.setId(null);
            sceneEditInfo.setScenePlusId(scenePlus.getId());
            sceneEditInfo.setSceneProId(null);
            sceneEditInfo.setTitle(scenePlus.getTitle());
            sceneEditInfoService.save(sceneEditInfo);

            SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoId);
            sceneEditInfoExt.setId(null);
            sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId());
            sceneEditInfoExt.setScenePlusId(scenePlus.getId());
            sceneEditInfoExt.setSceneProId(null);
            sceneEditInfoExtService.save(sceneEditInfoExt);

            SceneEditControls sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfoId);
            sceneEditControls.setId(null);
            sceneEditControls.setEditInfoId(sceneEditInfo.getId());
            sceneEditControlsService.save(sceneEditControls);

            List<Surveillance> list = surveillanceService.list(new LambdaQueryWrapper<Surveillance>().eq(Surveillance::getNum, oldNum));
            if (!Objects.isNull(list)) {
                list.stream().forEach(item -> {
                    item.setNum(newNum);
                    item.setId(null);
                    surveillanceService.save(item);
                });
            }

            if(SceneSourceUtil.isLaser(scenePlus.getSceneSource())){  //深时复制
                laserService.copy(oldNum,newNum,newDataSource,true);
            }else {
                laserService.cloudPointBuild(oldNum,newNum);
            }

            try {
                //重新生成编辑页基础设置二维码
                sceneCommonService.createNewQrCode(SceneVersionType.V4.code(),sceneEditInfoExt.getShareLogoImg(),newNum,plusExt.getWebSite());
                //copyDataSource
                //cn.hutool.core.io.FileUtil.copyContent(new File(oldDataSource),new File(newDataSource),true);

                this.copyOssAndNas(oldNum,newNum);

                //修改 oss status.json ,nas scene.json
                String targetData = String.format(SceneResourcePath.DATA_VIEW_PATH,newNum);
                sceneCommonService.updateOssJson(targetData,oldNum,newNum,"status.json");

                sceneCommonService.updateNasSceneJson(targetData,oldNum,newNum,scenePlus.getTitle(),"v4","scene.json");

                if(SceneSourceUtil.isLaser(scenePlus.getSceneSource())){  //深时复制
                    laserService.copy(oldNum,newNum,newDataSource,false);
                }
                String redisKey = RedisKeyUtil.laserCopyLock + newNum;
                long startTime = new Date().getTime();
                long startTime2 = new Date().getTime();
                while (redisUtil.hasKey(redisKey) ){     //深时场景复制同步锁
                    long waitTime = new Date().getTime();
                    if(waitTime - startTime2 >1000){
                        startTime2  = new Date().getTime();
                        log.info("等待激光复制业务结束----:{}",(waitTime - startTime) /1000);
                    }
                }

                scenePlus.setSceneStatus(-2);
                this.updateById(scenePlus);
                sceneCopyLogService.saveByNum(oldNum,newNum,scenePlus.getUserId());

                String redisSceneLock = String.format(RedisKeyUtil.copySpaceLock,cameraDetail.getCameraId());
                startTime = new Date().getTime();
                startTime2 = new Date().getTime();
                while(redisUtil.hasKey(redisSceneLock)){
                    long waitTime = new Date().getTime();
                    if(waitTime - startTime2 >1000){
                        startTime2  = new Date().getTime();
                        log.info("等待相机增加容量复制业务结束----:{}，{}",(waitTime - startTime) /1000,cameraDetail.getCameraId());
                    }
                }
                redisUtil.set(redisSceneLock,"1",60*60);
                Boolean checkSpace = cameraDetailService.checkSpace(cameraDetail.getCameraId(),cameraType);
                if(!checkSpace){
                    sceneProService.lockOrUnLockScenes(new ArrayList<>(),Arrays.asList(scenePlus.getNum()),-2,cameraType.getIsLaser(),null);
                }
                cameraDetailService.addUsedSpace(cameraDetail,plusExt.getSpace());
                redisUtil.del(redisSceneLock);

            }catch (Exception e){
                log.error("复制场景失败-V4-sceneNum:{},error:{}",oldNum,e);
                scenePlus.setSceneStatus(-1);
                this.updateById(scenePlus);
                throw new BusinessException(ResultCodeMsg.FAILURE_CODE_400006, ResultCodeMsg.FAILURE_MSG_400006);
            }
        }, executor).whenComplete((reslut, e) -> {
            log.info("copy-success-");
        });


        return scenePlus.getId();
    }

    private  void copyOssAndNas(String oldNum,String newNum){
        // 拷贝场景编辑资源
        String oldEditPath = String.format(SceneResourcePath.EDIT_PATH_v4, oldNum);
        String newEditPath = String.format(SceneResourcePath.EDIT_PATH_v4, newNum);
        fYunFileServiceInterface.copyFileInBucket(oldEditPath, newEditPath);

        // 拷贝场景展示资源
        String oldViewPath = String.format(SceneResourcePath.VIEW_PATH_v4, oldNum);
        String newViewPath = String.format(SceneResourcePath.VIEW_PATH_v4, newNum);
        fYunFileServiceInterface.copyFileInBucket(oldViewPath, newViewPath);

        //复制计算结果文件
        String oldResultPath = String.format(SceneResourcePath.SCENE_RESULT_DATA_PATH, oldNum);
        String newResultPath = String.format(SceneResourcePath.SCENE_RESULT_DATA_PATH, newNum);
        fYunFileServiceInterface.copyFileInBucket(oldResultPath, newResultPath);

        // 拷贝本地资源
        String oldPath = SceneResourcePath.nasBasePath + oldNum;
        String newPath = SceneResourcePath.nasBasePath + newNum;
        if(new File(oldPath).exists()){
            FileUtil.copyContent(new File(oldPath), new File(newPath),true);
        }
        String oldPath_v4 = SceneResourcePath.nasBasePath_v4 + oldNum;
        String newPath_v4 = SceneResourcePath.nasBasePath_v4 + newNum;
        if(new File(oldPath_v4).exists()){
            FileUtil.copyContent(new File(oldPath_v4), new File(newPath_v4),true);
        }
    }


    @Override
    public Result pageSceneWithHouseType(RequestScene param, String token) {
        User ssoUser = userService.getByToken(token);
        Page<ScenePlus> page = this.page(new Page<>(param.getPageNum(), param.getPageSize()),
                new LambdaQueryWrapper<ScenePlus>().eq(ScenePlus::getUserId, ssoUser.getId()).eq(ScenePlus::getHouseType, CommonStatus.YES.code().intValue()));
        List<SceneVo> records = null;
        if(page.getRecords().size() > 0){
            records = page.getRecords().parallelStream().map(plus -> {
                return SceneVo.builder().createTime(plus.getCreateTime())
                        .id(plus.getId()).roamingPointUrl(this.createRoamingPoint(plus.getNum())).sceneName(plus.getTitle())
                        .num(plus.getNum()).updateTime(plus.getUpdateTime()).userId(plus.getUserId()).build();
            }).collect(Collectors.toList());
        }

        PageInfo pageInfo = PageInfo.PageInfo(page);
        pageInfo.setList(records);

        return Result.success(pageInfo);
    }

    @Override
    public Result getArPathByNum(String num) {
        ScenePro scenePro = sceneProService.getByNum(num);
        if(Objects.nonNull(scenePro)){
            return Result.success("成功", String.format("images/images%s/ar", num));
        }
        ScenePlus scenePlus = this.getByNum(num);
        if(Objects.nonNull(scenePlus)){
            return Result.success("成功", String.format(UploadFilePath.IMG_VIEW_PATH, num) + "ar");
        }

        return Result.failure(ErrorCode.FAILURE_CODE_5005.code(), ErrorCode.FAILURE_CODE_5005.message());
    }

    private String createRoamingPoint(String num){
        String roamingPointOssPath = String.format(UploadFilePath.DATA_VIEW_PATH, num) + "roamingPoint.json";
        String url = null;
        try {
            if(!fYunFileServiceInterface.fileExist(roamingPointOssPath)){
                String visionModeldataPath = ConstantFilePath.SCENE_V4_PATH + num + File.separator + "vision.modeldata";
                String visionModeldataOssPath = String.format(UploadFilePath.IMG_VIEW_PATH, num) + "vision.modeldata";
                fYunFileServiceInterface.downloadFile(visionModeldataOssPath, visionModeldataPath);
                String visionTxtLocalPath = ConstantFilePath.SCENE_V4_PATH + num + File.separator + "vision.txt";
                CreateObjUtil.convertVisionmodeldataToTxt(visionModeldataPath, visionTxtLocalPath);
                String roamingPoint = RoamingPointUtil.createRoamingPoint(num, visionTxtLocalPath);
                if (StrUtil.isNotEmpty(roamingPoint)) {
                    fYunFileServiceInterface.uploadFile(roamingPoint.getBytes(StandardCharsets.UTF_8), roamingPointOssPath);
                    url = fyunHost + roamingPointOssPath;
                }
            }else{
                url = fyunHost + roamingPointOssPath;
            }
        }catch (Exception e){
            log.warn("生成roamingPointJson出错，num:" + num, e);
        }

        return url;
    }


    @Override
    public Long getCountByCameraId(Long cameraId,Boolean status) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScenePlus::getCameraId,cameraId);
        if(status){
            wrapper.eq(ScenePlus::getSceneStatus,-2);
        }
        return this.count(wrapper);
    }

    @Override
    public Long getCountByCameraId(Long cameraId) {
        LambdaQueryWrapper<ScenePlus> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ScenePlus::getCameraId,cameraId);
        wrapper.in(ScenePlus::getSceneStatus,0,-2);
        return this.count(wrapper);
    }

    @Override
    public Long getSpaceSumByCameraId(Long cameraId) {
        return this.getBaseMapper().getSpaceSumByCameraId(cameraId);
    }

    @Override
    public List<SceneNumVo> getGroupBySceneSouceV4(Long userId) {
        return this.getBaseMapper().getGroupBySceneSouceV4(userId);

    }

    @Override
    public List<SceneNumVo> getGroupBySceneSouceV3(Long userId) {
        return this.getBaseMapper().getGroupBySceneSouceV3(userId);

    }

    @Override
    public List<ScenePlus> getCountByUserIdAndSceneSource(Long userId, List<Integer> sceneSourceList, Integer isObj) {
        return this.getBaseMapper().getCountByUserIdAndSceneSource(userId,sceneSourceList,isObj);
    }

    @Override
    public Page<RelicsSceneInitQueueDTO> pageLaserScene(Page<RelicsSceneInitQueueDTO> objectPage, LaserSceneParam param) {
        return this.getBaseMapper().pageLaserScene(objectPage,param);
    }

    @Override
    public Long getCountByAndIsObj(Long userId, List<String> sourceList, Integer isObj,Integer isLaser) {
        return this.getBaseMapper().getCountByAndIsObj(userId,sourceList,isObj, isLaser);
    }

    @Override
    public List<SceneVo> getVoByNumList(List<String> numList) {
        return this.getBaseMapper().getVoByNumList(numList);
    }
}
