package com.tofly.scada.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tofly.scada.entity.*;
import com.tofly.scada.entity.dto.ScadaMoreQuery;
import com.tofly.scada.entity.dto.ScadaQuery;
import com.tofly.scada.entity.vo.ScadaVo;
import com.tofly.scada.entity.vo.StatisticsScadaVo;
import com.tofly.scada.mapper.AllocationMapper;
import com.tofly.scada.mapper.ScadaHistorySynMapper;
import com.tofly.scada.mapper.ScadaReportMapper;
import com.tofly.scada.mapper.StatisticsScadaMapper;
import com.tofly.scada.service.StatisticsScadaService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author haiqiu
 * @description 针对表【STATISTICS_SCADA】的数据库操作Service实现
 * @createDate 2022-05-10 15:55:35
 */
@Service
public class StatisticsScadaServiceImpl extends ServiceImpl<StatisticsScadaMapper, StatisticsScada> implements StatisticsScadaService {
    private final Logger logger = LoggerFactory.getLogger(StatisticsScadaServiceImpl.class);

    @Autowired
    private AllocationMapper allocationMapper;

    @Autowired
    private ScadaReportMapper reportMapper;

    @Autowired
    @Lazy
    private ScadaServiceImpl scadaService;

    @Autowired
    private ScadaHistorySynServiceImpl scadaHistorySynService;

    @Autowired
    private ScadaMonitorServiceImpl scadaMonitorService;

    /**
     * 根据开始结束时间获取月份表集合
     * @param start 开始时间
     * @param end 结束时间
     * @return 月份表集合
     */
    public List<String> getMouthTables(Date start, Date end) {
        List<Date> dates = ScadaServiceImpl.findDates(start, end, 1);
        if (!CollectionUtils.isEmpty(dates)) {
            List<String> tables = new ArrayList<>();
            for (Date date : dates) {
                //存储到分表月份
                String tableName = scadaService.createMonths(date == null ? new Date() : date);
                tables.add(tableName);
            }
            return tables;
        }
        return null;
    }

    @Override
    public Page<StatisticsScadaVo> getPageData(Page page, ScadaQuery scadaQuery, Boolean isPage) {
        if (scadaQuery.getIsStatisticsScada() == null) {
            scadaQuery.setIsStatisticsScada(false);
        }
        //isGeTrendData 是否为趋势分析查询条件,主要是更换查询条件，此处主要是更换表查询 SCADA_HISTORY  STATISTICS_SCADA
        QueryWrapper<ScadaQuery> queryWrapper = getQuery(scadaQuery, scadaQuery.getIsStatisticsScada());
        if (scadaQuery.getIsStatisticsScada()){
            if(isPage){
                return baseMapper.pageList(page, queryWrapper,"STATISTICS_SCADA");
            }else{
                Page<StatisticsScadaVo> pageDb = new Page<>();
                List<StatisticsScadaVo> list=  baseMapper.statisticsList(page, queryWrapper,"STATISTICS_SCADA");
                pageDb.setRecords(list);
                return pageDb;
            }

        }else {

//            List<String> da = new ArrayList<>();
//            da.add("SCADA_HISTORY");
            return scadaService.selectHistoryByMouthsTables("SCADA_HISTORY",page,scadaQuery,isPage);
        }
//        scadaService.selectHistoryByMouthsTables(getMouthTables(scadaQuery.getStart(),scadaQuery.getEnd()),page,queryWrapper);
//        return baseMapper.pageList(page, queryWrapper, scadaQuery.getIsStatisticsScada() ? "STATISTICS_SCADA" : "SCADA_HISTORY");
    }

    @Override
    public Page geTrendData(Page page, ScadaQuery scadaQuery, Boolean isPage,Boolean isExport) {
        QueryWrapper<ScadaQuery> queryWrapper = getQuery(scadaQuery, false);
        if (isPage) {
            Page<ScadaVo> scadaHistory = baseMapper.pageListNotStatistics(page, queryWrapper, "SCADA_HISTORY");
//            Page<ScadaVo> scadaHistory = baseMapper.selectHistoryByMouthsTables(getMouthTables(scadaQuery.getStart(),
//                    scadaQuery.getEnd()), page, queryWrapper);
            scadaValueHandle(scadaHistory.getRecords());
            return scadaHistory;
        } else {
            List<ScadaVo> scadaHistory = baseMapper.selectListDataNotStatistics(queryWrapper, "SCADA_HISTORY");
//            List<ScadaVo> scadaHistory = baseMapper.selectHistoryByMouthsTablesNoPage(getMouthTables(scadaQuery.getStart(),
//                    scadaQuery.getEnd()), queryWrapper);
            scadaValueHandle(scadaHistory);

            if(isExport){
                page.setRecords(scadaHistory);
                return page;
            }else{
                Page<StatisticsScadaVo> voPage = new Page<>();
                voPage.setRecords(calculationScadaData(scadaHistory, scadaQuery.getTimeTotal(), scadaQuery.getStart(), scadaQuery.getEnd()));
                voPage.setTotal(scadaHistory.size());
                voPage.setSize(scadaHistory.size());
                return voPage;
            }
        }
    }


    /**
     * 特殊值处理
     * @param scadaHistory
     */
    private void scadaValueHandle(List<ScadaVo> scadaHistory) {
        //特殊值处理
        for (ScadaVo scadaVo : scadaHistory) {
            if (scadaVo.getValue().startsWith(".")) {
                String value = scadaVo.getValue();
                scadaVo.setValue("0" + value);
            }
        }
    }

    @Override
    public int delete(Integer dateType, List<String> codes, Date start, Date end) {
        return baseMapper.deleteByTime(dateType, codes, start, end);
    }


    private List<StatisticsScadaVo> calculationScadaData(List<ScadaVo> scadaHistory, Integer excess, Date start, Date end) {
        //有数据就分组
        if (!CollectionUtils.isEmpty(scadaHistory)) {
            Map<String, List<ScadaVo>> listMap = scadaHistory.stream().filter(scada -> scada.getCode() != null && scada.getValue() != null)
                    .collect(Collectors.groupingBy(ScadaVo::getCode));

            List<StatisticsScadaVo> statisticsScadaVos = new ArrayList<>();
            for (Map.Entry<String, List<ScadaVo>> entity : listMap.entrySet()) {
                StatisticsScadaVo statisticsScadaVo = new StatisticsScadaVo();
                logger.info("有数据分组计算相关....");
                List<ScadaVo> scadaList = entity.getValue();
                double average = scadaList.stream().map(scada -> Double.parseDouble(scada.getValue()))
                        .mapToDouble(Double::shortValue).average().getAsDouble();
                double max = scadaList.stream().map(scada -> Double.parseDouble(scada.getValue()))
                        .mapToDouble(Double::shortValue).max().getAsDouble();
                double min = scadaList.stream().map(scada -> Double.parseDouble(scada.getValue()))
                        .mapToDouble(Double::shortValue).min().getAsDouble();
                double sum = scadaList.stream().map(scada -> Double.parseDouble(scada.getValue()))
                        .mapToDouble(Double::shortValue).sum();

                statisticsScadaVo.setCode(scadaList.get(0).getCode());
                statisticsScadaVo.setValue(scadaList.get(scadaList.size() - 1).getValue());
                statisticsScadaVo.setAve(average);
                statisticsScadaVo.setMax(max);
                statisticsScadaVo.setMin(min);
                statisticsScadaVo.setFold(sum);
                statisticsScadaVo.setStartTime(start);
                statisticsScadaVo.setEndTime(end);
                statisticsScadaVo.setStatisticsType(null);
                //设置分组子数据
                statisticsScadaVo.setScadaVos(scadaList);

                //查询指标报警上下限计算合格率相关
                logger.info("查询指标报警上下限计算合格率相关....");
                QueryWrapper<Allocation> allocationQueryWrapper = new QueryWrapper<>();
                allocationQueryWrapper.eq("VARIABLE_CODE", scadaList.get(0).getCode());
                List<Allocation> allocations = allocationMapper.selectList(allocationQueryWrapper);
                if (!CollectionUtils.isEmpty(allocations)) {
                    Set<Long> reportIdSets = ScadaServiceImpl.getReportIdSets(allocations);
                    logger.info("获取模版ID集合：{}....", reportIdSets);
                    if (!CollectionUtils.isEmpty(reportIdSets)) {
                        logger.info("计算指标报警上下限计算合格率相关....");
                        QueryWrapper<ScadaReport> scadaReportQueryWrapper = new QueryWrapper<>();
                        scadaReportQueryWrapper.in("ID", reportIdSets);
                        List<ScadaReport> scadaReports = reportMapper.selectList(scadaReportQueryWrapper);
                        if (!CollectionUtils.isEmpty(scadaReports)) {
                            //赋值报警列表
                            statisticsScadaVo.setScadaReports(scadaReports);
                            //超标次数
                            int errorCount = 0;
                            //不合格超标时长
                            long errorTimeTotal = 0;
                            //超标总时长
//                            long timeTotal = (scadaList.get(scadaList.size() - 1).getScadaTime().getTime()
//                                    - scadaList.get(0).getScadaTime().getTime()) / 1000;
                            //错误时间
                            Date errorTime;
                            for (ScadaVo scada : scadaList) {
                                for (ScadaReport report : scadaReports) {
                                    if (scada.getValue() == null){
                                        break;
                                    } else if(report.getReportLower() == null || report.getReportUpper() == null) {
                                        continue;
                                    }
                                    if (Double.parseDouble(scada.getValue()) > Double.parseDouble(report.getReportLower())
                                            && Double.parseDouble(scada.getValue()) < Double.parseDouble(report.getReportUpper())) {
                                        errorCount ++;
                                        errorTime = DateUtil.parse(scada.getScadaTime(), "yyyy-MM-dd HH:mm:ss");
                                        if(errorTime != null){
                                            errorTimeTotal += errorTime.getTime() / 1000;
                                        }
                                        break;
                                    }
                                }
                            }
                            //合格率
                            long totalCount = scadaList.size();
                            float pass = 100.00f;
                            if(totalCount != 0 && errorCount != 0){
                                long passCount = totalCount - errorCount;
                                pass = new BigDecimal(passCount).divide(BigDecimal.valueOf(totalCount),4,RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).floatValue();
                            }
                            statisticsScadaVo.setErrorCount(errorCount);
                            statisticsScadaVo.setErrorTimeTotal(errorTimeTotal);
                            statisticsScadaVo.setPass(pass);
                            statisticsScadaVo.setTimeTotal(errorTimeTotal);
                        }
                    }
                    statisticsScadaVo.setDisplayName(allocations.get(0).getDisplayName());
                    statisticsScadaVo.setDataType(String.valueOf(allocations.get(0).getType()));
                }
                //TODO 暂时全部返回，后续调整
//                if (excess != null && statisticsScadaVo.getErrorTimeTotal()!=null && statisticsScadaVo.getErrorTimeTotal() >= excess) {
//                    statisticsScadaVos.add(statisticsScadaVo);
//                } else if (excess == null) {
                statisticsScadaVos.add(statisticsScadaVo);
//                }
            }
            return statisticsScadaVos;
        }
        return null;
    }


    /**
     * 构建查询条件
     *
     * @param scadaQuery    查询实体
     * @param isGeTrendData 是否为趋势分析查询条件
     * @return
     */
    public QueryWrapper<ScadaQuery> getQuery(ScadaQuery scadaQuery, Boolean isGeTrendData) {
        QueryWrapper<ScadaQuery> queryWrapper = new QueryWrapper<>();
        if (!CollectionUtils.isEmpty(scadaQuery.getCodes())) {
            queryWrapper.in("scada.CODE", scadaQuery.getCodes());
        }
        if (!StringUtils.isEmpty(scadaQuery.getCode())) {
            queryWrapper.eq("scada.CODE", scadaQuery.getCode());
        }
        if (!CollectionUtils.isEmpty(scadaQuery.getDeviceIds())) {
            queryWrapper.in("manage.ID", scadaQuery.getDeviceIds());
        }
        if (scadaQuery.getDataTypeIds() != null) {
            Set<String> typeIds = scadaQuery.getDataTypeIds().stream().filter(Objects::nonNull)
                    .map(String::valueOf).collect(Collectors.toSet());
            queryWrapper.in("allocation.TYPE", typeIds);
        }
        if (scadaQuery.getStart() != null && scadaQuery.getEnd() != null) {
            if (scadaQuery.getStart().getTime() > scadaQuery.getEnd().getTime()) {
                throw new RuntimeException("开始时间不能大于结束时间");
            }
            if (isGeTrendData) {
                if (scadaQuery.getStatisticsType() != null) {
                    queryWrapper.eq("scada.STATISTICS_TYPE", scadaQuery.getStatisticsType());
                }
                queryWrapper.ge("scada.START_TIME", scadaQuery.getStart())
                        .le("scada.END_TIME", scadaQuery.getEnd());
                queryWrapper.orderByAsc("scada.START_TIME");
            } else {
                queryWrapper.between("scada.SCADA_TIME", scadaQuery.getStart(), scadaQuery.getEnd());
//                queryWrapper.orderByAsc("scada.SCADA_TIME");
            }
        }
        //如果是趋势分析查询
        if (isGeTrendData) {
            if (scadaQuery.getStart() == null && scadaQuery.getEnd() == null) {
                throw new RuntimeException("该接口时间范围为必填项");
            }
            //如果没有传入code就默认查询全部
            if (!StringUtils.hasText(scadaQuery.getCode()) && CollectionUtils.isEmpty(scadaQuery.getCodes())) {
                List<Allocation> allocations = allocationMapper.selectList(null);
                if (!CollectionUtils.isEmpty(allocations)) {
                    Set<String> codes = allocations.stream().filter(allocation -> allocation.getVariableCode() != null)
                            .map(Allocation::getVariableCode).collect(Collectors.toSet());
                    queryWrapper.in("scada.CODE", codes);
                }
            }
        }

        return queryWrapper;
    }

    /**
     * 获取指标报警的最低等级下限和最高等级上限
     * @param scadaReportList 报警信息列表
     * @return
     */
    private Map<String,Double> getReportUpperLower(List<ScadaReport> scadaReportList){
        if(scadaReportList == null || scadaReportList.size() == 0){
            return null;
        }
        Map<String,Double> res = new HashMap<>();
        Double reportLower = null;
        Double reportUpper = null;
        double mid;
        for (ScadaReport scadaReport : scadaReportList) {
            if(scadaReport.getReportUpper() != null && scadaReport.getReportLower() != null){
                if(reportLower == null){
                    reportLower = Double.parseDouble(String.valueOf(scadaReport.getReportLower()));
                }else {
                    mid = Double.parseDouble(String.valueOf(scadaReport.getReportLower()));
                    if(reportLower.compareTo(mid) > 0){
                        reportLower = mid;
                    }
                }
                if(reportUpper == null){
                    reportUpper = Double.parseDouble(String.valueOf(scadaReport.getReportUpper()));
                }else {
                    mid = Double.parseDouble(String.valueOf(scadaReport.getReportUpper()));
                    if(reportUpper.compareTo(mid) < 0){
                        reportUpper = mid;
                    }
                }
            }
        }
        if(reportLower == null || reportUpper == null){
            return null;
        }
        res.put("reportLower",reportLower);
        res.put("reportUpper",reportUpper);
        return res;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertBacthHistoryTemp(ScadaMoreQuery scadaMoreQuery) {
        if (scadaMoreQuery == null) {
            return;
        }
        List<ScadaHistorySyn> historyTempList = scadaMoreQuery.getHistoryTempList();
        if (historyTempList == null || historyTempList.size() == 0) {
            return;
        }
         int size=1000;
        if (historyTempList.size() <= size) {
            scadaHistorySynService.saveOrUpdateBatch(historyTempList);

        }else{
            int times = historyTempList.size() / size;
            for (int i = 0; i <= times; i++) {
                scadaHistorySynService.saveOrUpdateBatch(historyTempList.subList(i * size, Math.min((i + 1) * size, historyTempList.size())));
            }
        }

        historyTempList.sort(Comparator.comparing(ScadaHistorySyn::getScadaTime).reversed());
        historyTempList = historyTempList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(ScadaHistorySyn::getCode))), ArrayList::new));
        List<ScadaMonitor> scadaMonitors = scadaMonitorService.list();
        List<String> scadaMonitorCodes = scadaMonitors.stream().map(ScadaMonitor::getCode).collect(Collectors.toList());
        List<ScadaHistorySyn> scadaMonitorUpdates = historyTempList.stream()
                .filter(e -> scadaMonitorCodes.contains(e.getCode())).collect(Collectors.toList());
        List<ScadaHistorySyn> scadaMonitorInserts = historyTempList.stream()
                .filter(e -> !scadaMonitorCodes.contains(e.getCode())).collect(Collectors.toList());
        List<ScadaMonitor> monitorNewList = new ArrayList<>();
        if (CollUtil.isNotEmpty(scadaMonitorInserts)) {
            scadaMonitorInserts.forEach(e -> {
                ScadaMonitor scadaMonitor = new ScadaMonitor();
                scadaMonitor.setCode(e.getCode());
                scadaMonitor.setScadaTime(e.getScadaTime());
                scadaMonitor.setUpdateTime(new Date());
                monitorNewList.add(scadaMonitor);
                scadaMonitors.removeIf(info -> e.getCode().equals(info.getCode()));
            });
        }
        if (CollUtil.isNotEmpty(scadaMonitors) && CollUtil.isNotEmpty(scadaMonitorUpdates)) {
            scadaMonitors.forEach(e -> {
                List<ScadaHistorySyn> scadaMonitorUpdatesFinal = scadaMonitorUpdates.stream()
                        .filter(info -> info.getCode().equals(e.getCode())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(scadaMonitorUpdatesFinal)) {
                    e.setValue(scadaMonitorUpdatesFinal.get(0).getValue());
                    e.setScadaTime(scadaMonitorUpdatesFinal.get(0).getScadaTime());
                    e.setUpdateTime(new Date());
                }
            });
        }
        if (CollUtil.isNotEmpty(monitorNewList)) {
            scadaMonitorService.saveBatch(monitorNewList);
        }
        if (CollUtil.isNotEmpty(scadaMonitors)) {
            scadaMonitorService.updateBatchById(scadaMonitors);
        }
        logger.info("同步历史时新增的实时数据：{}", JSON.toJSONString(monitorNewList));
    }
}
