package com.tofly.xrtygis.service.impl;

import cn.hutool.http.ContentType;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tofly.xrtygis.custom.util.DateTimeUtil;
import com.tofly.xrtygis.custom.util.service.IFileInfoService;
import com.tofly.xrtygis.entity.NoiseLoggerInfo;
import com.tofly.xrtygis.entity.TfDeviceArchiveManage;
import com.tofly.xrtygis.entity.vo.hwm.error.HwmErrorBeanVo;
import com.tofly.xrtygis.entity.vo.hwm.error.HwmErrorVo;
import com.tofly.xrtygis.entity.vo.hwm.noise.Recording;
import com.tofly.xrtygis.entity.vo.hwm.noise.RecordingBeanVo;
import com.tofly.xrtygis.entity.vo.hwm.site.*;
import com.tofly.xrtygis.exception.BusinessException;
import com.tofly.xrtygis.service.NoiseJobService;
import com.tofly.xrtygis.service.NoiseLoggerInfoService;
import com.tofly.xrtygis.service.TfDeviceArchiveManageService;
import com.tofly.xrtygis.util.CoordTransform;
import com.tofly.xrtygis.util.HttpClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service("noiseJobService")
@Slf4j
public class NoiseJobServiceImpl implements NoiseJobService {

    @Value("${hwm.username}")
    private String username;
    @Value("${hwm.password}")
    private String password;
    @Value("${hwm.software}")
    private String software;
    @Value("${hwm.loggerapi}")
    private String loggerapi;
    @Value("${hwm.recordingsapi}")
    private String recordingsapi;
    @Value("${hwm.getrecordingsapi}")
    private String getrecordingsapi;
    @Value("${hwm.filePath}")
    private String filePath;
    @Autowired
    private TfDeviceArchiveManageService deviceArchiveManageService;
    @Autowired
    private NoiseLoggerInfoService noiseLoggerInfoService;
    @Autowired
    private IFileInfoService iFileInfoService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendNoiseLoggerInfo() {
        // 获取获取站点记录仪实时接口信息
        // 请求头封装
        Map<String, String> headerMap = new HashMap<>(12);
        // 请求参数封装
        Map<String, Object> map = new HashMap<>(12);
        map.put("username", username);
        map.put("password", password);
        map.put("software", software);
        map.put("leakstatus", true);
        try {
            HttpResponse httpResponse = HttpClientUtil.doGet(loggerapi, headerMap, map);
            JSONObject jsonObject = sendXml(httpResponse);
            if (null != jsonObject) {
                SiteNoiseBeanVo siteNoiseBeanVo = JSON.parseObject(jsonObject.toString(), SiteNoiseBeanVo.class);
                synchronized (this) {
                    disNoiseLoggerInfo(siteNoiseBeanVo);
                }
            }
        } catch (BusinessException b) {
            log.error("获取站点记录仪实时接口信息异常编码:{},说明:{}", b.getRetCd(), b.getMsgDes());
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendNoiseInfo() {
        // 获取获取记录仪噪音数据实时接口信息
        // 请求头封装
        Map<String, String> headerMap = new HashMap<>(12);
        // 请求参数封装
        Map<String, Object> map = new HashMap<>(12);
        map.put("username", username);
        map.put("password", password);
        map.put("software", software);
        // 获取当前时间开始日字符串 格式dd/mm/yyyy
        String todayBeginDate = DateTimeUtil.getLocalDateTime(DateTimeUtil.getTodayBeginTime(), DateTimeUtil.DAY_MONTH_YEAR);
        map.put("startdate", todayBeginDate);
        try {
            HttpResponse httpResponse = HttpClientUtil.doGet(recordingsapi, headerMap, map);
            JSONObject jsonObject = sendXml(httpResponse);
            log.info("同步结束HWM噪音录音相关文件返回信息{}",httpResponse.body());
            if (null != jsonObject) {
                RecordingBeanVo recordingBeanVo = JSON.parseObject(jsonObject.toString(), RecordingBeanVo.class);
                synchronized (this) {
                    disNoiseInfo(recordingBeanVo);
                }

            }
        } catch (BusinessException b) {
            log.error("获取记录仪噪音数据实时接口信息异常编码:{},说明:{}", b.getRetCd(), b.getMsgDes());
        }
    }

    /**
     * 文件请求处理
     *
     * @param noiseMap
     */
    public void sendNoiseFileInfo(Map<String, String> noiseMap) {

        // 获取获取记录仪噪音数据文件实时接口信息
        // 请求头封装
        Map<String, String> headerMap = new HashMap<>(12);
        // 请求参数封装
        Map<String, Object> map = new HashMap<>(12);
        map.put("username", username);
        map.put("password", password);
        map.put("software", software);
        noiseMap.forEach((k, v) -> {
            // 文件id
            map.put("id", v);
            HttpResponse httpResponse = HttpClientUtil.doGet(getrecordingsapi, headerMap, map);
            InputStream inputStream = httpResponse.bodyStream();
            // 新增文件到ftp
            String fileName = k + v;
            MultipartFile multipartFile = getMultipartFile(inputStream, fileName+".wav");
            List<MultipartFile> multipartFileList = new ArrayList<>();
            multipartFileList.add(multipartFile);
            StringBuilder builder = iFileInfoService.saveFileList(multipartFileList, filePath, "TF_NOISE_LOGGER_INFO_D");
            // 根据返回的file表中文件id 存入对应噪音记录最新数据中 每天一条
            List<NoiseLoggerInfo> list = noiseLoggerInfoService.list(new QueryWrapper<NoiseLoggerInfo>().lambda().eq(NoiseLoggerInfo::getMobileNumber, k));
            if(null!=list && list.size()>0){
               // 获取最新数据
                Optional<NoiseLoggerInfo> max = list.stream().max(Comparator.comparingDouble(i -> i.getLastValueDate().getTime()));
                NoiseLoggerInfo noiseLoggerInfo = max.get();
                noiseLoggerInfo.setFileId(builder.toString());
                noiseLoggerInfoService.updateById(noiseLoggerInfo);
            }



        });


    }

    /**
     * 执行请求获取响应体并释放资源
     */
    public static JSONObject sendXml(HttpResponse execute) {
        String contentType = execute.header("content-type");
        if (contentType.contains(ContentType.TEXT_XML.getValue())) {
            JSONObject jsonObject = JSONUtil.xmlToJson(execute.body());
            HwmErrorBeanVo hwmErrorVo = JSON.parseObject(jsonObject.toString(), HwmErrorBeanVo.class);
            HwmErrorVo error = hwmErrorVo.getError();
            if (null != error) {
                // 异常抛出 日志记录
                throw new BusinessException(Integer.valueOf(error.getErrorMessage().getResponseCode() + ""), error.getErrorMessage().getId());
            }
            return jsonObject;
        }
        return null;
    }


    /**
     * 噪音记录仪设备信息,实时数据处理
     *
     * @param vo
     */

    public void disNoiseLoggerInfo(SiteNoiseBeanVo vo) {
        // 存入设备档案
        Loggers loggers = vo.getLoggers();
        List<Logger> logger = loggers.getLogger();
        Set<String> collect = logger.stream().map(Logger::getMobileNumber).collect(Collectors.toSet());
        // 获取对应噪音设备编码-id map
        Map<String, Long> codeIdMap = deviceArchiveManageService.list(new QueryWrapper<TfDeviceArchiveManage>().lambda().in(TfDeviceArchiveManage::getCode, collect)).stream().
                collect(Collectors.
                        toMap(TfDeviceArchiveManage::getCode, TfDeviceArchiveManage::getId, (item1, item2) -> item1));

        List<TfDeviceArchiveManage> updateList = new ArrayList<>();
        List<TfDeviceArchiveManage> saveList = new ArrayList<>();
        List<NoiseLoggerInfo> noiseLoggerInfoList = new ArrayList<>();
        // 当前设备状态置为离线 设备离线列表
        Set<String> offlineSet = new HashSet<>();
//        // 获取当前设备和记录最新自增id 录音数据没有并发量 不做单独批量新增
//        Long noId = noiseLoggerInfoService.selectMaxId("TF_NOISE_LOGGER_INFO_D");
//        Long deId = noiseLoggerInfoService.selectMaxId("TF_DEVICE_ARCHIVE_MANAGE");
        logger.forEach(l -> {
            TfDeviceArchiveManage v = new TfDeviceArchiveManage();
            v.setCode(l.getMobileNumber());
            v.setName(l.getSiteName());
            v.setMakeDate(l.getDateCreated());
            v.setDeviceType("4");
            v.setManufacturer("英国HWM");
            v.setModelSpecification(l.getType());
            v.setType("2");
            v.setStatus("1");
            // 转为大地2000
            double[] doubles = CoordTransform.transformGCJ02ToWGS84(l.getLongitude(),l.getLatitude());
            log.info("经纬度坐标转换{}",JSON.toJSONString(doubles));
            v.setLongitude(doubles[0]);
            v.setLatitude(doubles[1]);
            v.setDescribe(l.getNotes());
            v.setCreateUser(0L);
            if (codeIdMap.containsKey(l.getMobileNumber())) {
                v.setId(codeIdMap.get(l.getMobileNumber()));
                updateList.add(v);
            } else {
                saveList.add(v);

            }
            Channels channels = l.getChannels();
            List<Channel> channel = channels.getChannel();
            channel.forEach(c -> {
                // 目前通道为1 泄露状态 业务目前使用,其他不使用
                if ("1".equals(c.getNumber())) {
                    NoiseLoggerInfo n = new NoiseLoggerInfo();
                    n.setMobileNumber(l.getMobileNumber());
                    n.setChannelName(c.getChannelName());
                    n.setMonitoringState(c.getLastValue());
                    n.setLastValueDate(c.getLastValueDate());
                    n.setSynchronisedTime(DateTimeUtil.convertLDTToDate(LocalDateTime.now()));
                    n.setNumberCode(c.getNumber());
                    // 获取当前噪音记录最新数据
                    NoiseLoggerInfo maxLastValueDateVo = noiseLoggerInfoService.getMaxLastValueDateVo(l.getMobileNumber());
                    // 当前最新数据不为空
                    if (null != maxLastValueDateVo) {
                        // 1.判断当前当前最新数据上传时间是否大于原有数据最新记录时间 新增
                        LocalDateTime date = DateTimeUtil.fromLong2LocalDateTime(c.getLastValueDate().getTime());
                        LocalDateTime loadDate = DateTimeUtil.fromLong2LocalDateTime(maxLastValueDateVo.getLastValueDate().getTime());
                        int i = date.compareTo(loadDate);
                        if (i > 0) {
                            noiseLoggerInfoList.add(n);
                        }
                        // 2.是否相差大于2天,若为两天则对应设备状态标识为离线
                        LocalDateTime now = LocalDateTime.now();
                        LocalDateTime time = date.plusDays(2);
                        int i1 = now.compareTo(time);
                        if (i1 > 0) {
                            offlineSet.add(l.getMobileNumber());
                        }
                    } else {
                        noiseLoggerInfoList.add(n);
                    }

                }

            });
        });
        // 判断是否有离线设备
        if (offlineSet.size() > 0) {
            updateList.forEach(info -> {
                if (offlineSet.contains(info.getCode())) {
                    info.setStatus("4");
                }
            });
            saveList.forEach(info -> {
                if (offlineSet.contains(info.getCode())) {
                    info.setStatus("4");
                }
            });
        }
        // 存入对应设备台账,噪音记录
        if (updateList.size() > 0) {
            deviceArchiveManageService.updateBatchById(updateList);
        }
        if (saveList.size() > 0) {
            saveList.forEach(info -> {
                deviceArchiveManageService.save(info);
            });
        }
        if (noiseLoggerInfoList.size() > 0) {
            noiseLoggerInfoList.forEach(info -> {
                noiseLoggerInfoService.save(info);
            });

        }


    }


    public void disNoiseInfo(RecordingBeanVo vo) {
        // 存储今日设备有对应噪音文件记录map key sms编码 value 录音文件id
        Map<String, String> noiseMap = new HashMap<>(12);
        List<Recording> recording = vo.getRecordings().getRecording();
        recording.forEach(info -> {
            String recordingType = info.getRecordingType();
            if ("3".equals(recordingType)) {
                noiseMap.put(info.getNumber(), info.getId());
            }
        });
        // 查询噪音文件,存入噪音记录数据中
        sendNoiseFileInfo(noiseMap);

    }


    /**
     * 获取封装得MultipartFile
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return MultipartFile
     */
    public MultipartFile getMultipartFile(InputStream inputStream, String fileName) {
        FileItem fileItem = createFileItem(inputStream, fileName);
        //CommonsMultipartFile是feign对multipartFile的封装，但是要FileItem类对象
        return new CommonsMultipartFile(fileItem);
    }


    /**
     * FileItem类对象创建
     *
     * @param inputStream inputStream
     * @param fileName    fileName
     * @return FileItem
     */
    public FileItem createFileItem(InputStream inputStream, String fileName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        String textFieldName = "file";
        FileItem item = factory.createItem(textFieldName, MediaType.MULTIPART_FORM_DATA_VALUE, true, fileName);
        int bytesRead = 0;
        byte[] buffer = new byte[10 * 1024 * 1024];
        OutputStream os = null;
        //使用输出流输出输入流的字节
        try {
            os = item.getOutputStream();
            while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            inputStream.close();
        } catch (IOException e) {
            log.error("Stream copy exception", e);
            throw new IllegalArgumentException("文件上传失败");
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("Stream close exception", e);

                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("Stream close exception", e);
                }
            }
        }

        return item;
    }
}
