package com.ho.filecenter.service.impl;

import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.util.DateUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ho.business.entity.Device;
import com.ho.business.entity.Station;
import com.ho.business.vo.DeviceTransfer;
import com.ho.business.vo.req.DeviceReqVO;
import com.ho.business.vo.req.point.PointCurveReq;
import com.ho.business.vo.resp.DeviceRespVO;
import com.ho.common.tools.constant.CommonConstant;
import com.ho.common.tools.entity.SimpleUser;
import com.ho.common.tools.exception.DataResult;
import com.ho.common.tools.service.RedisService;
import com.ho.common.tools.util.PageResult;
import com.ho.common.tools.util.PageUtils;
import com.ho.filecenter.entity.Otaupgrad;
import com.ho.filecenter.feignclient.BusinessFeignClient;
import com.ho.filecenter.mapper.OtaupgradMapper;
import com.ho.filecenter.service.FileService;
import com.ho.filecenter.service.OtaupgradService;
import com.ho.filecenter.vo.OtaFileVO;
import com.ho.filecenter.vo.ScheduleReqVo;
import com.ho.filecenter.vo.UpdateRecords;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.CRC32;

/**
 * @author gyan
 * @desc: TODO
 * @DateTime: 2023/8/2 10:33
 */
@Service
@Slf4j
public class OtaupgradServiceImpl implements OtaupgradService {

    @Value("${path.local}")
    String localPath;

    @Autowired
    OtaupgradMapper otaupgradMapper;

    @Autowired
    FileService fileService;

    @Autowired
    RedisService redisService;

    @Autowired
    BusinessFeignClient businessFeignClient;

    @Override
    public PageResult<Otaupgrad> page(OtaFileVO vo) {
        PointCurveReq pointCurveReq = new PointCurveReq();
        pointCurveReq.setIds(vo.getStationIdList());
        pointCurveReq.setDeviceName(vo.getDeviceName());
        pointCurveReq.setDeviceType(vo.getDeviceType());
        DataResult<List<Device>> deviceDataResult = businessFeignClient.getByStationId(pointCurveReq);
        List<Otaupgrad> list = new ArrayList<>();
        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
        if (deviceDataResult.isSuccess() && !deviceDataResult.getData().isEmpty()) {
            List<Otaupgrad> otaupgradList = otaupgradMapper.selectAll(vo);
            List<Device> data = deviceDataResult.getData();
            for (Device datum : data) {
                Otaupgrad otaupgradValue = new Otaupgrad();
                otaupgradValue.setSrcId(datum.getSrcId());
                otaupgradValue.setDeviceName(datum.getDeviceName());
                otaupgradValue.setDeviceType(datum.getDeviceType());
                otaupgradValue.setStationId(datum.getStationId());
                DataResult<Station> stationDataResult = businessFeignClient.selectByStationId(datum.getStationId());
                Station station = stationDataResult.getData();
                otaupgradValue.setStationName(station.getName());
                DeviceReqVO deviceReqVO = new DeviceReqVO();
                deviceReqVO.setSrcId(datum.getSrcId());
                deviceReqVO.setStationId(station.getId());
                DataResult<DeviceRespVO> listDataResult = businessFeignClient.selectBySrcIdAndStationId(deviceReqVO);
                DeviceRespVO deviceRespVO = listDataResult.getData();
                String key = deviceRespVO.getDeviceType() + ":" + station.getId() + ":" + datum.getSrcId();
                if (redisService.hasKey(key)) {
                    //版本号
                    DeviceTransfer totalChargeTransfer = (DeviceTransfer) redisService.hget(key, "fwareVerRead");
                    if (totalChargeTransfer != null) {
                        otaupgradValue.setVersion(totalChargeTransfer.getValue().toString());
                    }
                }
                list.add(otaupgradValue);
            }
            //将查出的重复数据剔除，留下最新版本 otaupgradList
            Map<Integer, List<Otaupgrad>> otaupgradMap = otaupgradList.stream().collect(Collectors.groupingBy(Otaupgrad::getSrcId));
            List<Otaupgrad> otaupgradNewList = new ArrayList<>();
            for (Otaupgrad otaupgrad : otaupgradList) {
                List<Otaupgrad> st1 = otaupgradMap.get(otaupgrad.getSrcId());
                if (CommonConstant.ONE != st1.size()) {
                    otaupgradNewList.add(st1.get(0));
                } else {
                    otaupgradNewList.add(st1.get(0));
                }
            }
            List<Otaupgrad> sList = otaupgradNewList.stream().distinct().collect(Collectors.toList());
            for (Otaupgrad otaupgrad : list) {
                for (Otaupgrad value : sList) {
                    if (otaupgrad.getSrcId().equals(value.getSrcId()) && otaupgrad.getStationId().equals(value.getStationId())) {
                        otaupgrad.setId(value.getId());
                        otaupgrad.setStationId(value.getStationId());
                        otaupgrad.setSrcId(value.getSrcId());
                        otaupgrad.setDeviceName(value.getDeviceName());
                        otaupgrad.setDeviceType(value.getDeviceType());
                        otaupgrad.setFileName(value.getFileName());
                        // otaupgrad.setVersion(value.getVersion());
                        otaupgrad.setUpdateCause(value.getUpdateCause());
                        otaupgrad.setUpdatesTime(DateUtil.formatDateTime(value.getUpdateTime()));
                        otaupgrad.setCreateTime(value.getCreateTime());
                    }
                }
            }
        }
        PageResult pageResult = PageUtils.getPageResult(new PageInfo<>(list));
        return pageResult;
    }

    @Override
    public List<UpdateRecords> selectHisFile(OtaFileVO vo) {
        List<UpdateRecords> list = new ArrayList<>();
        List<Otaupgrad> otaupgradList = otaupgradMapper.selectAll(vo);
        for (int i = 0; i < otaupgradList.size(); i++) {
            Otaupgrad otaupgrad = otaupgradList.get(i);
            UpdateRecords updateRecords = new UpdateRecords();
            BeanUtils.copyProperties(otaupgrad, updateRecords);
            if (i + 1 < otaupgradList.size()) {
                Otaupgrad otaupgradValue = otaupgradList.get(i + 1);
                if (otaupgradValue.getUpdateTime() != null) {
//                    Date updateTime = otaupgradValue.getUpdateTime();
//                    DateFormat dateformat = new SimpleDateFormat(CommonConstant.DATE);
//                    String format = dateformat.format(updateTime);
                    updateRecords.setFrontUpdateTime(DateUtils.format(otaupgradValue.getUpdateTime(), CommonConstant.DATE));
                }
                updateRecords.setFrontVersion(otaupgradValue.getVersion());
            }
            list.add(updateRecords);
        }
        DeviceReqVO deviceReqVO = new DeviceReqVO();
        deviceReqVO.setSrcId(vo.getSrcId());
        deviceReqVO.setStationId(vo.getStationId());
        DataResult<DeviceRespVO> listDataResult = businessFeignClient.selectBySrcIdAndStationId(deviceReqVO);
        DeviceRespVO data = listDataResult.getData();
        String key = data.getDeviceType() + ":" + vo.getStationId() + ":" + vo.getSrcId();
        if (redisService.hasKey(key)) {
            //版本号
            DeviceTransfer totalChargeTransfer = (DeviceTransfer) redisService.hget(key, "fwareVerRead");
            if (totalChargeTransfer != null) {
                UpdateRecords updateRecords = list.get(CommonConstant.ZERO);
                updateRecords.setVersion(totalChargeTransfer.getValue().toString());
            }
        }
        return list;
    }

    @Override
    public void upgradation(MultipartFile file, OtaFileVO vo, SimpleUser user) throws ParseException {
        String userId = user.getUserId();
        String name = file.getOriginalFilename();
        //先把电站呵设备解出来
        Map<String, Integer> map = new HashMap<>();
        List<Integer> stationList = new ArrayList<>();
        List<String> deviceList = new ArrayList<>();
        for (String stationAndDevice : vo.getStationIdAndDeviceList()) {
            String[] temp;
            String delimiter = "-";// 指定分割字符
            temp = stationAndDevice.split(delimiter);
            int stationId = Integer.parseInt(temp[0]);
            for (int i = 1; i < temp.length; i++) {
                deviceList.add(temp[i]);
                map.put(temp[i], stationId);
            }
            stationList.add(stationId);
        }
        List<Otaupgrad> list = new ArrayList<>();
        if (!stationList.isEmpty()) {
            Set<String> keys = map.keySet();
            for (String key : keys) {
                Integer value = map.get(key);
                DeviceReqVO deviceReqVO = new DeviceReqVO();
                deviceReqVO.setSrcId(Integer.valueOf(key).intValue());
                deviceReqVO.setStationId(value);
                DataResult<DeviceRespVO> listDataResult = businessFeignClient.selectBySrcIdAndStationId(deviceReqVO);
                DeviceRespVO data = listDataResult.getData();
                Otaupgrad otaupgrad = new Otaupgrad();
                otaupgrad.setFileName(name);
                otaupgrad.setDeviceName(data.getDeviceName());
                otaupgrad.setSrcId(Integer.valueOf(key));
                otaupgrad.setStationId(value);
                otaupgrad.setUpdateCause(vo.getUpdateCause());
                otaupgrad.setUpdateTime(DateUtil.parse(vo.getUpdateTime()));
                otaupgrad.setVersion(vo.getVersion());
                otaupgrad.setCreateTime(new Date());
                otaupgrad.setUpdateName(userId);
                list.add(otaupgrad);
            }
        }
        if (!list.isEmpty()) {
            int i = otaupgradMapper.insertBatch(list);
        }

    }

    @Override
    public void tage(MultipartFile file, OtaFileVO vo) throws Exception {
        List<Integer> stationList = new ArrayList<>();
        for (String stationAndDevice : vo.getStationIdAndDeviceList()) {
            String[] temp;
            String delimiter = "-";// 指定分割字符
            temp = stationAndDevice.split(delimiter);
            int stationId = Integer.parseInt(temp[0]);
            stationList.add(stationId);
        }
        //生成配置文件
        getConfigFile(file, vo);
        //去获取生成的配置文件
        String fileName = "config";
        Path path3 = Paths.get(localPath + File.separator + fileName);
        String srcFilepath = localPath + File.separator + file.getOriginalFilename();
        //创建文件
        File uploadFile = new File(srcFilepath);
        //判断是否存在文件目录,没有就创建
        uploadFile.mkdirs();
        //上传文件到指定路径
        file.transferTo(uploadFile);
        List pathList = Arrays.asList(Paths.get(srcFilepath), path3);
        //输出文件压缩结果
        String name = "devUpgradePack.tar.gz";
        String value = localPath + File.separator + name;
        Path output = Paths.get(localPath + File.separator + name);
        //OutputStream输出流、BufferedOutputStream缓冲输出流
        //GzipCompressorOutputStream是gzip压缩输出流
        //TarArchiveOutputStream打tar包输出流（包含gzip压缩输出流）
        try (OutputStream fOut = Files.newOutputStream(output);
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
            for (Object path : pathList) {
                Path path1 = (Path) path;
                //该文件不是目录或者符号链接
                if (!Files.isRegularFile(path1)) {
                    throw new IOException("Support only file!");
                }
                //将该文件放入tar包，并执行gzip压缩
                TarArchiveEntry tarEntry = new TarArchiveEntry(
                        path1.toFile(),
                        path1.getFileName().toString());
                tOut.putArchiveEntry(tarEntry);
                Files.copy(path1, tOut);

                tOut.closeArchiveEntry();
            }
            //for循环完成之后，finish-tar包输出流
            tOut.finish();
            fileService.fileUploadDevice(name, stationList, value);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public Long getCRCValue(MultipartFile file) throws Exception {
        byte[] bytes = file.getBytes();
        CRC32 crc32 = new CRC32();
        crc32.update(bytes);
        long crcValue = crc32.getValue();
        return crcValue;
    }


    public void getConfigFile(MultipartFile file, OtaFileVO vo) throws Exception {
        //先把电站呵设备解出来
        List<String> deviceList = new ArrayList<>();
        for (String stationAndDevice : vo.getStationIdAndDeviceList()) {
            String[] temp;
            String delimiter = "-";// 指定分割字符
            temp = stationAndDevice.split(delimiter);
            for (int i = 1; i < temp.length; i++) {
                deviceList.add(temp[i]);
            }
        }
        String srcId = "";
        if (CommonConstant.ONE < deviceList.size()) {
            for (int i = 0; i < deviceList.size(); i++) {
                srcId = srcId + deviceList.get(i) + ",";
            }
        } else {
            srcId = deviceList.get(CommonConstant.ZERO) + ",";

        }
        String name = file.getOriginalFilename();
        byte[] bytes = file.getBytes();
        Long crcValue = getCRCValue(file);
        String version = vo.getVersion();
        String newStr = vo.getUpdateTime().replace(':', '-');
        String update = newStr.replace(' ', '-');
        String fileName = "config";
        Path path = Paths.get(localPath + "/" + fileName);
        // 使用newBufferedWriter创建文件并写文件
        // 这里使用了try-with-resources方法来关闭流，不用手动关闭
        String lineSeparator = System.getProperty("line.separator");
        try (BufferedWriter writer =
                     Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
            writer.write("PARAM_NAME\t\tVALUE\t\t\t\t\t\t\tDESC\n");
            writer.write("FILENAME \t\t" + name + " \t\t\t\t\t升级文件名，最长64个字节" + lineSeparator);
            writer.write("FILELEN \t\t" + bytes.length + "\t\t\t\t\t\t\t升级文件长度  单位：字节" + lineSeparator);
            writer.write("CRC32 \t\t" + crcValue + "\t\t升级文件CRC32校验码" + lineSeparator);
            writer.write("FILEVER\t\t" + version + "\t\t\t\t\t\t\t升级文件版本号，最长4字节" + lineSeparator);
            writer.write("DEVID\t\t\t" + srcId.substring(0, srcId.length() - 1) + "\t\t升级机器id" + lineSeparator);
            writer.write("UPGRADE_TIME\t" + update + "\t机器升级时间点 YYYY-MM-DD-HH-mm-ss" + lineSeparator);
        }
    }


    @Override
    public List<ScheduleReqVo> getUpgradeProgress(OtaFileVO vo) {
        List<ScheduleReqVo> list = new ArrayList<>();
        Map<String, Integer> map = new HashMap<>();
        List<Integer> stationList = new ArrayList<>();
        List<String> deviceList = new ArrayList<>();
        for (String stationAndDevice : vo.getStationIdAndDeviceList()) {
            String[] temp;
            String delimiter = "-";// 指定分割字符
            temp = stationAndDevice.split(delimiter);
            int stationId = Integer.parseInt(temp[0]);
            for (int i = 1; i < temp.length; i++) {
                deviceList.add(temp[i]);
                map.put(temp[i], stationId);
            }
            stationList.add(stationId);
        }
        Map<Integer, String> stationDeviceType = new HashMap<>();
        for (String stationIdAndDeviceType : vo.getStationIdAndDeviceTypeList()) {
            String[] temp;
            String delimiter = "-";// 指定分割字符
            temp = stationIdAndDeviceType.split(delimiter);
            int stationId = Integer.parseInt(temp[0]);
            for (int i = 1; i < temp.length; i++) {
                deviceList.add(temp[i]);
                stationDeviceType.put(stationId, temp[i]);
            }
        }
        for (String device : deviceList) {
            if (!device.equals("null")) {
                ScheduleReqVo scheduleReqVo = new ScheduleReqVo();
                if (map.containsKey(device)) {
                    Integer stationId = map.get(device);
                    DeviceReqVO deviceReqVO = new DeviceReqVO();
                    deviceReqVO.setSrcId(Integer.valueOf(device).intValue());
                    deviceReqVO.setStationId(stationId);
                    DataResult<DeviceRespVO> listDataResult = businessFeignClient.selectBySrcIdAndStationId(deviceReqVO);
                    DeviceRespVO data = listDataResult.getData();

                    String key = data.getDeviceType() + ":" + stationId + ":" + device;
                    scheduleReqVo.setStationId(stationId);
                    scheduleReqVo.setSrcId(Integer.valueOf(device));
                    if (redisService.hasKey(key)) {
                        //升级状态
                        DeviceTransfer socTransfer = (DeviceTransfer) redisService.hget(key, "mstatusread");
                        if (socTransfer != null) {
                            scheduleReqVo.setUpgradeResult(socTransfer.getValue());
                        } else {
                            scheduleReqVo.setUpgradeResult(new BigDecimal(0));
                        }
                        //进度百分比
                        DeviceTransfer sohTransfer = (DeviceTransfer) redisService.hget(key, "upgradeProcession");
                        if (sohTransfer != null) {
                            scheduleReqVo.setUpgradeProcession(sohTransfer.getValue());
                        } else {
                            scheduleReqVo.setUpgradeProcession(new BigDecimal(0));
                        }
                        //版本号
                        DeviceTransfer totalChargeTransfer = (DeviceTransfer) redisService.hget(key, "fwareVerRead");
                        if (totalChargeTransfer != null) {
                            scheduleReqVo.setFwareVerRead(totalChargeTransfer.getValue());
                        } else {
                            scheduleReqVo.setFwareVerRead(new BigDecimal(2.3).setScale(2, BigDecimal.ROUND_HALF_UP));
                        }
                    } else {
                        scheduleReqVo.setUpgradeResult(new BigDecimal(0));
                        scheduleReqVo.setUpgradeProcession(new BigDecimal(0));
                        scheduleReqVo.setFwareVerRead(new BigDecimal(2.3).setScale(2, BigDecimal.ROUND_HALF_UP));
                    }
                }
                list.add(scheduleReqVo);
            }
        }
        return list;
    }
}