package com.njcn.csdevice.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.njcn.access.api.AskDeviceDataFeignClient;
import com.njcn.access.enums.AccessResponseEnum;
import com.njcn.access.pojo.dto.file.FileDto;
import com.njcn.access.utils.ChannelObjectUtil;
import com.njcn.access.utils.MqttUtil;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.csdevice.enums.AlgorithmResponseEnum;
import com.njcn.csdevice.service.DeviceFtpService;
import com.njcn.csharmonic.pojo.vo.MakeUpVo;
import com.njcn.oss.utils.FileStorageUtil;
import com.njcn.redis.pojo.enums.AppRedisKey;
import com.njcn.redis.utils.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author xy
 */
@Service
@RequiredArgsConstructor
public class DeviceFtpServiceImpl implements DeviceFtpService {

    private final AskDeviceDataFeignClient askDeviceDataFeignClient;
    private final RedisUtil redisUtil;
    private final ChannelObjectUtil channelObjectUtil;
    private final MqttUtil mqttUtil;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public MakeUpVo askDeviceRootPath(String nDid) {
        MakeUpVo makeUpVo = new MakeUpVo();
        try {
            boolean mqttClient = judgeClientOnline(nDid);
            if (!mqttClient){
                throw new BusinessException(AccessResponseEnum.MISSING_CLIENT);
            }
            redisUtil.delete(AppRedisKey.DEVICE_ROOT_PATH + nDid);
            askDeviceDataFeignClient.askDeviceRootPath(nDid);
            for (int i = 0; i <= 3; i++) {
                Thread.sleep(1000);
                Object object = redisUtil.getObjectByKey(AppRedisKey.DEVICE_ROOT_PATH + nDid);
                if (Objects.nonNull(object)) {
                    makeUpVo.setPrjDataPath(object.toString());
                    makeUpVo.setType("dir");
                    break;
                }
            }
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        return makeUpVo;
    }

    @Override
    public List<MakeUpVo> askDeviceFileOrDir(String nDid, String name, String type) {
        List<MakeUpVo> result = new ArrayList<>();
        try {
            boolean mqttClient = judgeClientOnline(nDid);
            if (!mqttClient){
                throw new BusinessException(AccessResponseEnum.MISSING_CLIENT);
            }
            // 删除旧的缓存信息
            redisUtil.delete(AppRedisKey.PROJECT_INFO + nDid);
            // 请求设备文件或目录信息
            askDeviceDataFeignClient.askDeviceFileOrDir(nDid, name);
            for (int i = 0; i <= 10; i++) {
                Thread.sleep(1000);
                // 从 Redis 获取对象
                Object object = redisUtil.getObjectByKey(AppRedisKey.PROJECT_INFO + nDid);
                if (object != null) {
                    // 根据类型处理不同的数据
                    processObject(result, object, type);
                    break;
                }
            }
        } catch (Exception e) {
            // 捕获特定异常并抛出运行时异常
            throw new BusinessException(e.getMessage());
        }
        if (CollUtil.isNotEmpty(result)) {
            result.stream().sorted(Comparator.comparing(MakeUpVo::getType)).collect(Collectors.toList());
        }
        return result;
    }

    @Override
    @Async
    public void downloadFile(String nDid, String name, Integer size, String fileCheck) {
        boolean mqttClient = judgeClientOnline(nDid);
        if (!mqttClient) {
            throw new BusinessException(AccessResponseEnum.MISSING_CLIENT);
        }
        //判断文件如果过大，不给下载
        if (size > 15728640) {
            throw new BusinessException("文件过大(超过15M),暂不支持下载!");
        }
        Object task = redisUtil.getObjectByKey("fileDowning:"+nDid);
        if (Objects.nonNull(task)) {
            throw new BusinessException(AlgorithmResponseEnum.FILE_DOWNLOADING);
        }
        Object task2 = redisUtil.getObjectByKey("uploading");
        if (Objects.nonNull(task2)) {
            throw new BusinessException(AlgorithmResponseEnum.FILE_BUSY);
        }
        try {
            redisUtil.delete("downloadFilePath:"+ nDid + name);
            askDeviceDataFeignClient.downloadFile(nDid,name,size,fileCheck).getData();
        } catch (Exception e) {
            redisUtil.delete("fileDowning:"+nDid);
            redisUtil.delete("fileCheck"+nDid+name);
            throw new BusinessException(e.getMessage());
        }
    }

    @Override
    public String getDownloadFilePath(String nDid, String name) {
        String result = null;
        Object object = redisUtil.getObjectByKey("downloadFilePath:" + nDid + name);
        if (Objects.nonNull(object)) {
            result = (String) object;
            redisUtil.delete("downloadFilePath:" + nDid + name);
        }
        redisUtil.delete("fileCheck"+nDid+name);
        redisUtil.delete("fileDowning:"+nDid);
        redisUtil.delete(AppRedisKey.FILE_PART.concat(name));
        redisUtil.delete("fileDownUserId"+nDid+name);
        return result;
    }

    @Override
    public boolean createFile(String nDid, String path) {
        boolean result = false;
        try {
            askDeviceDataFeignClient.createFolder(nDid,path);
            Thread.sleep(3000);
            String key = "createDir"+ nDid;
            Integer value = Integer.parseInt(redisUtil.getObjectByKey(key).toString());
            if (Objects.equals(value,200)) {
                result = true;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e.getMessage());
        }
        return result;
    }

    @Override
    public boolean deleteFile(String nDid, String path) {
        boolean result = false;
        try {
            askDeviceDataFeignClient.deleteFolder(nDid,path);
            Thread.sleep(3000);
            String key = "deleteDir"+ nDid;
            Integer value = Integer.parseInt(redisUtil.getObjectByKey(key).toString());
            if (Objects.equals(value,200)) {
                result = true;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e.getMessage());
        }
        return result;
    }

    private void processObject(List<MakeUpVo> result, Object object, String type) {
        if ("dir".equals(type)) {
            List<FileDto.DirInfo> projectInfoList = channelObjectUtil.objectToList(object, FileDto.DirInfo.class);
            projectInfoList.forEach(item -> {
                MakeUpVo vo = createMakeUpVoFromDir(item);
                result.add(vo);
            });
        } else {
            FileDto.FileInfo fileInfo = channelObjectUtil.objectToSingleObject(object, FileDto.FileInfo.class);
            MakeUpVo vo = createMakeUpVoFromFile(fileInfo);
            result.add(vo);
        }
    }

    private MakeUpVo createMakeUpVoFromDir(FileDto.DirInfo item) {
        MakeUpVo vo = new MakeUpVo();
        vo.setPrjDataPath(item.getName());
        vo.setType(item.getType());
        vo.setSize(item.getSize());
        if (item.getTime() == 0) {
            vo.setStartTime("/");
        } else {
            LocalDateTime dateTime = Instant.ofEpochMilli((item.getTime()-8*3600)*1000).atZone(ZoneId.systemDefault()).toLocalDateTime();
            String formattedDate = dateTime.format(formatter);
            vo.setStartTime(formattedDate);
        }
        return vo;
    }

    private MakeUpVo createMakeUpVoFromFile(FileDto.FileInfo fileInfo) {
        MakeUpVo vo = new MakeUpVo();
        vo.setPrjDataPath(fileInfo.getName());
        if (fileInfo.getFileTime() != 0) {
            LocalDateTime dateTime = Instant.ofEpochMilli((fileInfo.getFileTime() - 8*3600) * 1000).atZone(ZoneId.systemDefault()).toLocalDateTime();
            String formattedDate = dateTime.format(formatter);
            vo.setStartTime(formattedDate);
            vo.setSize(fileInfo.getFileSize());
        }
        vo.setFileCheck(fileInfo.getFileCheck());
        return vo;
    }

    public boolean judgeClientOnline(String nDid) {
        //判断客户端是否在线
        String clientName = "NJCN-" + nDid.substring(nDid.length() - 6);
        return mqttUtil.judgeClientOnline(clientName);
    }


}
