package com.ruoyi.system.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.dto.*;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.paramet.BerthingPointQuery;
import com.ruoyi.system.paramet.FloatPointQuery;
import com.ruoyi.system.paramet.StopPointQuery;
import com.ruoyi.system.paramet.TimePointQuery;
import com.ruoyi.system.service.BerthingService;
import com.ruoyi.system.service.MqttGateWayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * classname: 断面信息
 * description  服务实现类
 */
@Service
@Slf4j
public class BerthingServiceImpl implements BerthingService {


    @Resource
    private SiteInfoMapper siteInfoMapper;

    @Resource
    private BerthingPointMapper berthingPointMapper;

    @Resource
    private BerthingPointConfigMapper berthingPointConfigMapper;

    @Resource
    private BerthingTimeConfigMapper berthingTimeConfigMapper;

    @Resource
    private BerthingFloatConfigMapper berthingFloatConfigMapper;

    @Resource
    private MqttGateWayService mqttGateWayService;


    @Override
    public ArrayList<PrintExcelDTO> uploadFile(MultipartFile file) {
        ArrayList<PrintExcelDTO> excelDateList = new ArrayList<>();
        try {
            if (file.isEmpty()) {
                log.error("文件为空");
                throw new BaseException("文件为空");
            }
            EasyExcel.read(file.getInputStream(), PrintExcelDTO.class, new ReadListener<PrintExcelDTO>() {
                @Override
                public void invoke(PrintExcelDTO printExcelDto, AnalysisContext analysisContext) {
                    excelDateList.add(printExcelDto);
                }
                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {}
            }).sheet().doRead();
            //排序
            excelDateList.sort(Comparator.comparingDouble(PrintExcelDTO::getX));
            return excelDateList;
        } catch (BaseException e){
            throw new BaseException(e.getMessage());
        } catch (Exception e){
            log.error("上传断面excel错误", e);
            throw new BaseException("上传断面excel错误");
        }
    }

    @Override
    public boolean insertBerthing(BerthingPointDTO berthingPointDTO){
        SiteInfo siteInfo = siteInfoMapper.queryById(berthingPointDTO.getSiteId());
        if (siteInfo == null) {
            throw new BaseException("站点不存在");
        }
        try {
            ArrayList<PrintExcelDTO> positions = berthingPointDTO.getPositions();
            positions.sort(Comparator.comparingDouble(PrintExcelDTO::getX));
            //设置他断面状态为无效
            berthingPointMapper.setBerthingStatus(siteInfo.getSiteId());
            BerthingPoint berthingPoint = new BerthingPoint();
            berthingPoint.setBerthingName(berthingPointDTO.getBerthingName());
            berthingPoint.setPositions(JSONUtil.toJsonStr(positions));
            berthingPoint.setSiteId(siteInfo.getSiteId());
            berthingPoint.setBerthingId(0L);
            berthingPoint.setCreateTime(new Date());
            berthingPoint.setIsDel(0);
            berthingPoint.setId(siteInfo.getId());
            berthingPoint.setStatus(1);
            boolean res = berthingPointMapper.insert(berthingPoint) > 0;
            //TODO 配置下发
            String topic = "down/" + siteInfo.getDeviceId() + "/section";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id",siteInfo.getId());
            requestMap.put("points",positions);
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}",topic,data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
            return res;
        } catch (Exception e){
            log.error("新增断面错误", e);
            throw new BaseException("新增断面错误");
        }
    }


    @Override
    public  Page<BerthingPoint> queryBerthing(BerthingPointQuery berthingPointQuery) {
        try {
            if (berthingPointQuery.getPage() == null || berthingPointQuery.getSize() == null) {
                berthingPointQuery.setPage(1L);
                berthingPointQuery.setSize(10L);
            }
            Page<SiteInfo> page = new Page<>(berthingPointQuery.getPage(), berthingPointQuery.getSize());
            return berthingPointMapper.queryAllByLimit(berthingPointQuery, page);

        } catch (Exception e) {
            log.error("查询站点错误", e);
            throw new BaseException("查询站点错误");
        }
    }

    @Override
    public BerthingPoint queryBerthingById(Long id) {
        return berthingPointMapper.queryById(id);
    }

    @Override
    public BerthingPoint queryBerthingBySiteId(Long siteId) {
        return berthingPointMapper.queryBySiteId(siteId);
    }

    @Override
    public Boolean deleteBerthingById(Long id) {
        BerthingPoint berthingPoint = berthingPointMapper.queryById(id);
        if (berthingPoint == null) {
            throw new BaseException("断面不存在");
        }
        BerthingPoint siteBerthingPoint = berthingPointMapper.queryBySiteId(berthingPoint.getSiteId());
        if (Objects.equals(berthingPoint.getBerthingId(), siteBerthingPoint.getBerthingId())) {
            throw new BaseException("最新断面不能删除");
        }
        berthingPointMapper.deleteById(id);
        return true;
    }

    @Override
    public boolean downFileById(Long id, HttpServletResponse response) {
        if (id == null) {
            throw new BaseException("断面ID不能为空");
        }
        BerthingPoint berthingPoint = berthingPointMapper.queryById(id);
        if (berthingPoint == null) {
            throw new BaseException("断面不存在");
        }
        return downloadExcel(berthingPoint, response);
    }

    @Override
    public boolean downFile(Long siteId, HttpServletResponse response) {
        if (siteId == null) {
            throw new BaseException("站点ID不能为空");
        }
        BerthingPoint berthingPoint = berthingPointMapper.queryBySiteId(siteId);
        if (berthingPoint == null) {
            throw new BaseException("没有断面数据");
        }
        return downloadExcel(berthingPoint, response);
    }

    private boolean downloadExcel(BerthingPoint berthingPoint, HttpServletResponse response) {
        try {
            List<PrintExcelDTO> positions = JSONUtil.toList(berthingPoint.getPositions(), PrintExcelDTO.class);
            response.setContentType("application/octet-stream; charset=utf-8");
            String today = new SimpleDateFormat("yyyyMMdd").format(new Date());
            String fileName = "downFile_".concat(today);
            // 设置导出头信息，文件名放在里面
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            // 设置自适应列宽，注册一个handler: LongestMatchColumnWidthStyleStrategy
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
            // 创建sheet并设置好标题
            WriteSheet detailSheet = EasyExcel.writerSheet(0, "断面数据").head(PrintExcelDTO.class).build();
            // 向sheet中写入数据
            excelWriter.write(positions, detailSheet);
            // 数据下载，完成后会自动关闭流
            excelWriter.finish();
            return true;
        } catch (Exception e){
            log.error("下载文件错误", e);
            throw new BaseException("下载文件错误");
        }
    }


    @Override
    public boolean addStopPrint(BerthingPointConfigDTO berthingPointConfigDTO) {
        SiteInfo siteInfo = siteInfoMapper.queryById(berthingPointConfigDTO.getSiteId());
        if (siteInfo == null) {
            throw new BaseException("站点不存在");
        }
        if (berthingPointConfigDTO.getStopId() == null) {
            berthingPointConfigDTO.setStopId(0L);
        }
        if(berthingPointConfigDTO.getPositions().size() != berthingPointConfigDTO.getFactors().size()){
            throw new BaseException("修正系数与停泊点位长度不匹配");
        }
        BerthingPoint berthingPoint = berthingPointMapper.queryBySiteId(berthingPointConfigDTO.getSiteId());
        if (berthingPoint == null) {
            throw new BaseException("有效的断面数据未找到");
        }
        boolean res;
        BerthingPointConfig berthingPointConfig = berthingPointConfigMapper.queryById(berthingPointConfigDTO.getStopId());
        try {
            ArrayList<PrintExcelDTO> positions = berthingPointConfigDTO.getPositions();
            if (berthingPointConfig == null) {
                //新增
                berthingPointConfig = new BerthingPointConfig();
                //先查询有无重复添加
                berthingPointConfig.setWlevel(berthingPointConfigDTO.getWlevel());
                berthingPointConfig.setSiteId(berthingPointConfigDTO.getSiteId());
                long count = berthingPointConfigMapper.count(berthingPointConfig);
                if (count > 0){
                    throw new BaseException("起测水位已存在");
                }
                BerthingPointConfig maxPlanid = berthingPointConfigMapper.queryMaxPlanid(berthingPointConfigDTO.getSiteId());
                if (maxPlanid == null) {
                    maxPlanid = new BerthingPointConfig();
                    maxPlanid.setPlanid(0L);
                }
                berthingPointConfig.setStopId(0L);
                berthingPointConfig.setSiteId(siteInfo.getSiteId());
                berthingPointConfig.setBerthingId(berthingPoint.getBerthingId());
                berthingPointConfig.setPositions(JSONUtil.toJsonStr(positions));
                berthingPointConfig.setPlanid(maxPlanid.getPlanid() + 1);
                berthingPointConfig.setWlevel(berthingPointConfigDTO.getWlevel());
                berthingPointConfig.setFactors(JSONUtil.toJsonStr(berthingPointConfigDTO.getFactors()));
                berthingPointConfig.setCreateTime(new Date());
                berthingPointConfig.setIsDel(0);
                berthingPointConfig.setStatus(1);
                berthingPointConfig.setId(siteInfo.getId());
                res =  berthingPointConfigMapper.insert(berthingPointConfig) > 0;
            } else {
                //先置旧停泊点策略无效 再新插入一条
                berthingPointConfig.setIsDel(1);
                berthingPointConfigMapper.update(berthingPointConfig);
                berthingPointConfig.setStopId(0L);
                berthingPointConfig.setIsDel(0);
                berthingPoint.setCreateTime(new Date());
                berthingPointConfig.setPositions(JSONUtil.toJsonStr(positions));
                berthingPointConfig.setWlevel(berthingPointConfigDTO.getWlevel());
                berthingPointConfig.setFactors(JSONUtil.toJsonStr(berthingPointConfigDTO.getFactors()));
                res =  berthingPointConfigMapper.insert(berthingPointConfig) > 0;
            }
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/stopPosition";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id",siteInfo.getId());
            requestMap.put("wlevel",berthingPointConfig.getWlevel());
            requestMap.put("planid",berthingPointConfig.getPlanid());
            requestMap.put("positions",positions);
            requestMap.put("factors",berthingPointConfigDTO.getFactors());
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}",topic,data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        } catch (BaseException e){
            throw new BaseException(e.getMessage());
        } catch (Exception e){
            log.error("新增或修改停泊点错误", e);
            throw new BaseException("新增或修改停泊点错误");
        }
        return res;
}

    @Override
    public Page<BerthingPointConfig> queryStopPrint(StopPointQuery stopPointQuery) {
        try {
            if (stopPointQuery.getPage() == null || stopPointQuery.getSize() == null) {
                stopPointQuery.setPage(1L);
                stopPointQuery.setSize(10L);
            }
            return berthingPointConfigMapper.queryAllByLimit(stopPointQuery, new Page<>(stopPointQuery.getPage(), stopPointQuery.getSize()));
        } catch (Exception e) {
            log.error("查询停泊点错误", e);
            throw new BaseException("查询停泊点错误");
        }

    }

    @Override
    public BerthingPointConfig queryStopPointsById(Long id) {
        return berthingPointConfigMapper.queryById(id);
    }

    @Override
    public Boolean deleteStopPointsById(Long id) {
        BerthingPointConfig berthingPointConfig = berthingPointConfigMapper.queryById(id);
        boolean res = berthingPointConfigMapper.deleteById(id) > 0;
        if (berthingPointConfig != null) {
            SiteInfo siteInfo = siteInfoMapper.queryById(berthingPointConfig.getSiteId());
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/removePlan";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id", siteInfo.getId());
            requestMap.put("planid", berthingPointConfig.getPlanid());
            requestMap.put("plantype", 0); //策略类型，0: 垂线停泊点；1: 时间计划；2: 水位变幅
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}", topic, data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        }
        return res;
    }

    @Override
    public boolean addTimePrint(BerthingTimeDTO berthingTimeDTO) {
        SiteInfo siteInfo = siteInfoMapper.queryById(berthingTimeDTO.getSiteId());
        if (siteInfo == null) {
            throw new BaseException("站点不存在");
        }
        if (berthingTimeDTO.getTimeId() == null) {
            berthingTimeDTO.setTimeId(0L);
        }
        boolean res;
        BerthingTimeConfig berthingTime = berthingTimeConfigMapper.queryById(berthingTimeDTO.getTimeId());
        try {
            ArrayList<String> times = berthingTimeDTO.getTimes();
            if (berthingTime == null) {
                berthingTime = new BerthingTimeConfig();
                //先查询有无重复添加
                berthingTime.setWlevel(berthingTimeDTO.getWlevel());
                berthingTime.setSiteId(siteInfo.getSiteId());
                long count = berthingTimeConfigMapper.count(berthingTime);
                if (count > 0){
                    throw new BaseException("起测水位已存在");
                }
                BerthingFloatConfig maxPlanidTimeConfig = berthingTimeConfigMapper.queryByMaxPlanid(siteInfo.getSiteId());
                if (maxPlanidTimeConfig == null) {
                    berthingTime.setPlanid(1L);
                } else {
                    berthingTime.setPlanid(maxPlanidTimeConfig.getPlanid() + 1);
                }
                //新增
                berthingTime.setTimeId(0L);
                berthingTime.setWlevel(berthingTimeDTO.getWlevel());
                berthingTime.setTimes(JSONUtil.toJsonStr(times));
                berthingTime.setCreateTime(new Date());
                berthingTime.setIsDel(0);
                berthingTime.setStatus(1);
                berthingTime.setId(siteInfo.getId());
                berthingTime.setType(berthingTimeDTO.getType());
                res =  berthingTimeConfigMapper.insert(berthingTime) > 0;
            } else {
                //更新
                berthingTime.setWlevel(berthingTimeDTO.getWlevel());
                berthingTime.setTimes(JSONUtil.toJsonStr(times));
                berthingTime.setType(berthingTimeDTO.getType());
                res =  berthingTimeConfigMapper.update(berthingTime) > 0;
            }
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/measureMode";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id",siteInfo.getId());
            requestMap.put("type",berthingTime.getType());
            requestMap.put("planid",berthingTime.getPlanid());
            requestMap.put("wlevel",berthingTime.getWlevel());
            requestMap.put("times",times);
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}",topic,data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        } catch (BaseException e){
            throw new BaseException(e.getMessage());
        } catch (Exception e){
            log.error("新增或修改时间策略错误", e);
            throw new BaseException("新增或修改时间策略错误");
        }
        return res;
    }

    @Override
    public Page<BerthingTimeConfig> queryTimePrint(TimePointQuery timePointQuery) {
        try {
            if (timePointQuery.getPage() == null || timePointQuery.getSize() == null) {
                timePointQuery.setPage(1L);
                timePointQuery.setSize(10L);
            }
            return berthingTimeConfigMapper.queryAllByLimit(timePointQuery, new Page<>(timePointQuery.getPage(), timePointQuery.getSize()));
        } catch (Exception e) {
            log.error("查询停泊点错误", e);
            throw new BaseException("查询停泊点错误");
        }

    }

    @Override
    public BerthingTimeConfig queryStopTimeById(Long id) {
        return berthingTimeConfigMapper.queryById(id);
    }

    @Override
    public boolean deleteStopTimeById(Long id) {
        BerthingTimeConfig berthingTimeConfig = berthingTimeConfigMapper.queryById(id);
        boolean res = berthingTimeConfigMapper.deleteById(id) > 0;
        if (berthingTimeConfig != null) {
            SiteInfo siteInfo = siteInfoMapper.queryById(berthingTimeConfig.getSiteId());
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/removePlan";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id", siteInfo.getId());
            requestMap.put("planid", berthingTimeConfig.getPlanid());
            requestMap.put("plantype", 1); //策略类型，0: 垂线停泊点；1: 时间计划；2: 水位变幅
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}", topic, data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        }
        return res;
    }

    @Override
    public boolean addFloatPrint(BerthingFloatDTO berthingFloatDTO) {
        SiteInfo siteInfo = siteInfoMapper.queryById(berthingFloatDTO.getSiteId());
        if (siteInfo == null) {
            throw new BaseException("站点不存在");
        }
        if (berthingFloatDTO.getFloatId() == null) {
           berthingFloatDTO.setFloatId(0L);
        }
        boolean res;
        BerthingFloatConfig berthingFloat = berthingFloatConfigMapper.queryById(berthingFloatDTO.getFloatId());
        try {
            if (berthingFloat == null) {
                //新增
                berthingFloat = new BerthingFloatConfig();
                berthingFloat.setWlevel(berthingFloatDTO.getWlevel());
                berthingFloat.setSiteId(siteInfo.getSiteId());
                long count = berthingFloatConfigMapper.count(berthingFloat);
                if (count > 0) {
                    throw new BaseException("起浮水位已存在");
                }
                BerthingFloatConfig maxPlanidFloatConfig = berthingFloatConfigMapper.queryByMaxPlanid(siteInfo.getSiteId());
                if (maxPlanidFloatConfig == null) {
                    berthingFloat.setPlanid(1L);
                } else {
                    berthingFloat.setPlanid(maxPlanidFloatConfig.getPlanid() + 1);
                }
                berthingFloat.setFloatId(0L);
                berthingFloat.setWlevel(berthingFloatDTO.getWlevel());
                berthingFloat.setCreateTime(new Date());
                berthingFloat.setIsDel(0);
                berthingFloat.setStatus(1);
                berthingFloat.setId(siteInfo.getId());
                berthingFloat.setTimespan(berthingFloatDTO.getTimespan());
                berthingFloat.setWlevelchange(berthingFloatDTO.getWlevelchange());
                berthingFloat.setType(berthingFloatDTO.getType());
                res = berthingFloatConfigMapper.insert(berthingFloat) > 0;
            } else {
                //更新
                berthingFloat.setType(berthingFloatDTO.getType());
                berthingFloat.setWlevel(berthingFloatDTO.getWlevel());
                berthingFloat.setTimespan(berthingFloatDTO.getTimespan());
                berthingFloat.setWlevelchange(berthingFloatDTO.getWlevelchange());
                res = berthingFloatConfigMapper.update(berthingFloat) > 0;
            }
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/addMode";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id", siteInfo.getId());
            requestMap.put("type", berthingFloat.getType());
            requestMap.put("planid", berthingFloat.getPlanid());
            requestMap.put("wlevel", berthingFloat.getWlevel());
            requestMap.put("timespan", berthingFloat.getTimespan());
            requestMap.put("wlevelchange", berthingFloat.getWlevelchange());
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}", topic, data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        }catch (BaseException e){
            throw new BaseException(e.getMessage());
        } catch (Exception e){
            log.error("新增或修改水位变幅策略错误", e);
            throw new BaseException("新增或修改水位变幅策略错误");
        }
        return res;
    }

    @Override
    public Page<BerthingFloatConfig> queryFloatPrint(FloatPointQuery floatPointQuery) {
        try {
            if (floatPointQuery.getPage() == null || floatPointQuery.getSize() == null) {
                floatPointQuery.setPage(1L);
                floatPointQuery.setSize(10L);
            }
            return berthingFloatConfigMapper.queryAllByLimit(floatPointQuery, new Page<>(floatPointQuery.getPage(), floatPointQuery.getSize()));
        } catch (Exception e) {
            log.error("查询水位变幅策略错误", e);
            throw new BaseException("查询水位变幅策略错误");
        }

    }

    @Override
    public BerthingFloatConfig queryStopWaterById(Long id) {
        return berthingFloatConfigMapper.queryById(id);
    }

    @Override
    public boolean deleteStopWaterById(Long id) {
        BerthingFloatConfig berthingFloat = berthingFloatConfigMapper.queryById(id);
        boolean res = berthingFloatConfigMapper.deleteById(id) > 0;
        if (berthingFloat != null) {
            SiteInfo siteInfo = siteInfoMapper.queryById(berthingFloat.getSiteId());
            //TODO 下发配置
            String topic = "down/" + siteInfo.getDeviceId() + "/removePlan";
            HashMap<String, Object> requestMap = new HashMap<>();
            requestMap.put("id", siteInfo.getId());
            requestMap.put("planid", berthingFloat.getPlanid());
            requestMap.put("plantype", 2); //策略类型，0: 垂线停泊点；1: 时间计划；2: 水位变幅
            String data = JSONUtil.toJsonStr(requestMap);
            log.info("mqtt发送数据：topic:{} data:{}", topic, data);
            mqttGateWayService.sendMessageToMqtt(data, topic);
        }
        return res;
    }

}
