package com.njcn.gather.monitor.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.detection.util.socket.CnSocketUtil;
import com.njcn.gather.device.pojo.enums.CheckResultEnum;
import com.njcn.gather.device.pojo.enums.CheckStateEnum;
import com.njcn.gather.device.pojo.enums.DevReportStateEnum;
import com.njcn.gather.device.pojo.po.PqDevSub;
import com.njcn.gather.monitor.mapper.PqMonitorMapper;
import com.njcn.gather.monitor.pojo.param.PqMonitorParam;
import com.njcn.gather.monitor.pojo.po.PqMonitor;
import com.njcn.gather.monitor.service.IPqMonitorService;
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
import com.njcn.gather.plan.pojo.enums.PlanReportStateEnum;
import com.njcn.gather.plan.pojo.po.AdPlan;
import com.njcn.gather.pojo.enums.DetectionResponseEnum;
import com.njcn.gather.storage.service.DetectionDataDealService;
import com.njcn.gather.storage.service.impl.DetectionDataServiceImpl;
import com.njcn.gather.system.dictionary.pojo.po.DictData;
import com.njcn.gather.system.dictionary.service.IDictDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author caozehui
 * @date 2024-12-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PqMonitorServiceImpl extends ServiceImpl<PqMonitorMapper, PqMonitor> implements IPqMonitorService {

    private final IDictDataService dictDataService;
    private final DetectionDataDealService detectionDataDealService;

    @Override
    public List<PqMonitor> listPqMonitorByDevIds(List<String> devIds) {
        if (CollUtil.isNotEmpty(devIds)) {
            return this.baseMapper.listByDevIds(devIds);
        }
        return CollUtil.empty(PqMonitor.class);
    }

    @Override
    public boolean addPqMonitorByDevId(String devId, List<PqMonitorParam> monitorList) {
        List<PqMonitor> pqMonitorList = BeanUtil.copyToList(monitorList, PqMonitor.class);
        pqMonitorList.forEach(pqMonitor -> pqMonitor.setDevId(devId));
        return this.saveBatch(pqMonitorList);
    }

    @Override
    @Transactional
    public boolean updatePqMonitorByDevId(String devId, List<PqMonitorParam> paramList) {
        // 先删除原有数据
//        this.deletePqMonitorByDevId(devId);

        // 添加新数据
        List<PqMonitor> pqMonitorList = BeanUtil.copyToList(paramList, PqMonitor.class);
        pqMonitorList.forEach(pqMonitor -> pqMonitor.setDevId(devId));

        List<PqMonitor> existedMonitorList = this.listPqMonitorByDevIds(Collections.singletonList(devId));

        Map<Integer, List<PqMonitor>> map = pqMonitorList.stream().collect(Collectors.groupingBy(PqMonitor::getNum));

        List<PqMonitor> newMonitorList = new ArrayList<>();
        map.forEach((num, monitorList) -> {
            PqMonitor pqMonitor = existedMonitorList.stream().filter(monitor -> monitor.getNum() == num).findFirst().orElse(null);

            if (ObjectUtil.isNotNull(pqMonitor)) {
                BeanUtil.copyProperties(monitorList.get(0), pqMonitor, "id", "realtimeResult", "statisticsResult", "recordedResult", "realtimeNum", "statisticsNum", "recordedNum", "resultType", "qualifiedNum");
                newMonitorList.add(pqMonitor);
                existedMonitorList.remove(pqMonitor);
            } else {
                newMonitorList.addAll(monitorList);
            }
        });

        // 同步更新设备的状态
        List<PqMonitor> enableCheckMonitorList = newMonitorList.stream().filter(pqMonitor -> pqMonitor.getCheckFlag() == 1).collect(Collectors.toList());
        List<PqMonitor> checkedMonitorList = enableCheckMonitorList.stream().filter(pqMonitor -> ObjectUtil.isNotNull(pqMonitor.getResultType())).collect(Collectors.toList());

        if (enableCheckMonitorList.size() == checkedMonitorList.size() && checkedMonitorList.size() > 0) {
            this.baseMapper.updateDeviceCheckState(devId, CheckStateEnum.CHECKED.getValue());
            this.baseMapper.updateDeviceReportRState(devId, DevReportStateEnum.NOT_GENERATED.getValue());
        }
        if (enableCheckMonitorList.size() > checkedMonitorList.size() && checkedMonitorList.size() > 0) {
            this.baseMapper.updateDeviceCheckState(devId, CheckStateEnum.UNCHECKED.getValue());
            this.baseMapper.updateDeviceReportRState(devId, DevReportStateEnum.NOT_GENERATED.getValue());
        }
        if (enableCheckMonitorList.size() > 0 && checkedMonitorList.size() == 0) {
            this.baseMapper.updateDeviceCheckState(devId, CheckStateEnum.UNCHECKED.getValue());
        }

        List<Integer> monitorResultList = checkedMonitorList.stream().map(pqMonitor -> {
            String resultType = pqMonitor.getResultType();
            DataSourceEnum dataSourceEnum = DataSourceEnum.ofByValue(resultType);
            switch (dataSourceEnum) {
                case REAL_DATA:
                    return pqMonitor.getRealtimeResult();
                case WAVE_DATA:
                    return pqMonitor.getRecordedResult();
                case MINUTE_STATISTICS_MAX:
                case MINUTE_STATISTICS_MIN:
                case MINUTE_STATISTICS_AVG:
                case MINUTE_STATISTICS_CP95:
                    return pqMonitor.getStatisticsResult();
                default:
                    break;
            }
            return null;
        }).collect(Collectors.toList());

        long qualifiedCount = monitorResultList.stream().filter(result -> result != null && result == 1).count();
        if (qualifiedCount == monitorResultList.size() && monitorResultList.size() > 0) {
            this.baseMapper.updateDeviceCheckResult(devId, CheckResultEnum.ACCORD.getValue());
        } else if (monitorResultList.size() > 0) {
            this.baseMapper.updateDeviceCheckResult(devId, CheckResultEnum.NOT_ACCORD.getValue());
        } else {
            this.baseMapper.updateDeviceCheckResult(devId, CheckResultEnum.UNCHECKED.getValue());
        }

        // 同步更新计划的状态
        AdPlan plan = this.baseMapper.getPlanByDevId(devId);
        if (ObjectUtil.isNotNull(plan)) {


            List<PqDevSub> devSubList = this.baseMapper.listDevSubByPlanId(plan.getId());

            List<PqDevSub> checkedDevSubList = devSubList.stream().filter(pqDevSub -> pqDevSub.getCheckState() == CheckStateEnum.CHECKED.getValue()).collect(Collectors.toList());
            boolean allUnchecked = devSubList.stream().allMatch(pqDevSub -> CheckStateEnum.UNCHECKED.getValue().equals(pqDevSub.getCheckState()));
            boolean allArchived = devSubList.stream().allMatch(pqDevSub -> pqDevSub.getCheckState() != null && pqDevSub.getCheckState() >= CheckStateEnum.DOCUMENTED.getValue());
            if (allArchived && CollUtil.isNotEmpty(devSubList)) {
                this.baseMapper.updatePlanCheckState(plan.getId(), CheckStateEnum.CHECKED.getValue());
            } else if (allUnchecked) {
                this.baseMapper.updatePlanCheckState(plan.getId(), CheckStateEnum.UNCHECKED.getValue());
            } else {
                this.baseMapper.updatePlanCheckState(plan.getId(), CheckStateEnum.CHECKING.getValue());
            }

            List<PqDevSub> accordDevSubList = checkedDevSubList.stream().filter(pqDevSub -> pqDevSub.getCheckResult() == CheckResultEnum.ACCORD.getValue()).collect(Collectors.toList());
            if (accordDevSubList.size() == checkedDevSubList.size() && checkedDevSubList.size() > 0) {
                this.baseMapper.updatePlanCheckResult(plan.getId(), CheckResultEnum.ACCORD.getValue());
            } else if (accordDevSubList.size() > 0) {
                this.baseMapper.updatePlanCheckResult(plan.getId(), CheckResultEnum.NOT_ACCORD.getValue());
            } else {
                this.baseMapper.updatePlanCheckResult(plan.getId(), CheckResultEnum.UNCHECKED.getValue());
            }

            List<PqDevSub> generatedDevSubList = checkedDevSubList.stream().filter(pqDevSub -> pqDevSub.getReportState() == DevReportStateEnum.GENERATED.getValue()).collect(Collectors.toList());
            if (generatedDevSubList.size() == checkedDevSubList.size() && checkedDevSubList.size() > 0) {
                this.baseMapper.updatePlanReportRState(plan.getId(), PlanReportStateEnum.REPORT_STATE_ALL_GENERATED.getValue());
            } else if (generatedDevSubList.size() > 0) {
                this.baseMapper.updatePlanReportRState(plan.getId(), PlanReportStateEnum.REPORT_STATE_PARTIALLY_GENERATED.getValue());
            } else {
                this.baseMapper.updatePlanReportRState(plan.getId(), PlanReportStateEnum.REPORT_STATE_NOT_GENERATED.getValue());
            }
        }
        this.removeByIds(existedMonitorList.stream().map(PqMonitor::getId).collect(Collectors.toList()));
        return this.saveOrUpdateBatch(newMonitorList);
    }

    @Override
    @Transactional
    public boolean deletePqMonitorByDevId(String devId) {
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", devId);
        return this.remove(wrapper);
    }

    @Override
    public void visualizeMonitor(List<PqMonitor> monitorList) {
        monitorList.forEach(monitor -> {
            DictData dictData = dictDataService.getDictDataById(monitor.getConnection());
            if (ObjectUtil.isNotNull(dictData)) {
                monitor.setConnection(dictData.getName());
            } else {
                throw new BusinessException(DetectionResponseEnum.CONNECTION_NOT_EXIST);
            }
        });
    }

    @Override
    public void reverseVisualizeMonitor(List<PqMonitor> monitorList) {
        monitorList.forEach(monitor -> {
            DictData dictData = dictDataService.getDictDataByName(monitor.getConnection());
            if (ObjectUtil.isNotNull(dictData)) {
                monitor.setConnection(dictData.getId());
            } else {
                throw new BusinessException(DetectionResponseEnum.CONNECTION_NOT_EXIST);
            }
        });
    }

    @Override
    public PqMonitor getByDevMonitorId(String devMonitorId) {
        String[] split = devMonitorId.split("_");
        return this.baseMapper.getByDevIdAndNum(split[0], Integer.valueOf(split[1]));
    }

    @Override
    public Double getRatedCurrent(String devMonitorId) {
        PqMonitor pqMonitor = this.getByDevMonitorId(devMonitorId);
        if (ObjectUtil.isNotNull(pqMonitor)) {
            String ct = pqMonitor.getCt();
            if (StrUtil.isNotBlank(ct) && ct.contains(StrUtil.COLON)) {
                String[] ctArray = ct.split(StrUtil.COLON);
                return Double.parseDouble(ctArray[1]);
            }
        }
        throw new BusinessException(DetectionResponseEnum.RATED_CT_ERROR);
    }

    @Override
    public Double getRatedVoltage(String devMonitorId) {
        PqMonitor pqMonitor = this.getByDevMonitorId(devMonitorId);
        if (ObjectUtil.isNotNull(pqMonitor)) {
            String pt = pqMonitor.getPt();
            if (StrUtil.isNotBlank(pt) && pt.contains(StrUtil.COLON)) {
                String[] ptArray = pt.split(StrUtil.COLON);
                return Double.parseDouble(ptArray[1]);
            }
        }
        throw new BusinessException(DetectionResponseEnum.RATED_PT_ERROR);
    }

    @Override
    @Transactional
    public boolean updateMonitorResult(String monitorId, List<String> adTypes, DataSourceEnum dataSourceEnum, Integer num, Integer waveNum, String code) {
        String[] split = monitorId.split(CnSocketUtil.SPLIT_TAG);
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", split[0])
                .eq("pq_monitor.Num", split[1])
                .last("LIMIT 1");
        PqMonitor monitor = this.getOne(wrapper);
        String resultType = monitor.getResultType();
        Integer qualifiedNum = monitor.getQualifiedNum();

        Integer newMonitorResult = CheckResultEnum.UNCHECKED.getValue();
        AtomicReference<Integer> newWaveNum = new AtomicReference<>(-1);
        switch (dataSourceEnum) {
            case WAVE_DATA:
                Map<Integer, Integer> waveNumResultMap = detectionDataDealService.getWaveNumResultMap(monitorId, adTypes, num, code);
                if (CollUtil.isEmpty(waveNumResultMap)) {
                    return true;
                }
                waveNumResultMap.forEach((key, value) -> {
                    if (CheckResultEnum.ACCORD.getValue().equals(value)) {
                        newWaveNum.set(key);
                    }
                });
                if (!newWaveNum.get().equals(-1)) {
                    newMonitorResult = CheckResultEnum.ACCORD.getValue();
                } else {
                    newWaveNum.set(waveNum);
                    newMonitorResult = waveNumResultMap.get(waveNum);
                    if (newMonitorResult == 2) {
                        newMonitorResult = CheckResultEnum.NOT_ACCORD.getValue();
                    }
                }
                break;
            default:
                newMonitorResult = detectionDataDealService.getMonitorResult(monitorId, adTypes, dataSourceEnum.getValue(), num, null, code);
                if (newMonitorResult == 2) {
                    newMonitorResult = CheckResultEnum.NOT_ACCORD.getValue();
                }
                if (newMonitorResult == 1) {
                    newMonitorResult = CheckResultEnum.ACCORD.getValue();
                }
                break;
        }

        boolean updateFlag = false;
        if (StrUtil.isBlank(resultType)) {
            updateFlag = true;
            if (newMonitorResult.equals(CheckResultEnum.ACCORD.getValue())) {
                qualifiedNum += 1;
            }
        } else {
            Integer oldMonitorResult = null;
            if (DataSourceEnum.REAL_DATA.getValue().equals(resultType)) {
                oldMonitorResult = monitor.getRealtimeResult();
            } else if (DataSourceEnum.WAVE_DATA.getValue().equals(resultType)) {
                oldMonitorResult = monitor.getRecordedResult();
            } else {
                oldMonitorResult = monitor.getStatisticsResult();
            }

            if (CheckResultEnum.ACCORD.getValue().equals(oldMonitorResult)) {
                if (CheckResultEnum.ACCORD.getValue().equals(newMonitorResult)) {
                    String oldNum = "";
                    if (DataSourceEnum.REAL_DATA.getValue().equals(resultType)) {
                        oldNum = monitor.getRealtimeNum();
                    } else if (DataSourceEnum.WAVE_DATA.getValue().equals(resultType)) {
                        oldNum = monitor.getRecordedNum();
                    } else {
                        oldNum = monitor.getStatisticsNum();
                    }
                    String[] split1 = oldNum.split(CnSocketUtil.SPLIT_TAG);
                    if (!split1[0].equals(num.toString())) {
                        qualifiedNum += 1;
                    }
                    updateFlag = true;
                }
            } else {
                updateFlag = true;
                if (CheckResultEnum.ACCORD.getValue().equals(newMonitorResult)) {
                    qualifiedNum += 1;
                }
            }
        }

        if (updateFlag) {
            monitor.setResultType(dataSourceEnum.getValue());

            switch (dataSourceEnum) {
                case REAL_DATA:
                    monitor.setRealtimeNum(String.valueOf(num));
                    monitor.setRealtimeResult(newMonitorResult);
                    monitor.setRecordedNum(null);
                    monitor.setRecordedResult(null);
                    monitor.setStatisticsNum(null);
                    monitor.setStatisticsResult(null);
                    break;
                case WAVE_DATA:
                    monitor.setRealtimeNum(null);
                    monitor.setRealtimeResult(null);
                    monitor.setRecordedNum(num + CnSocketUtil.SPLIT_TAG + newWaveNum.get());
                    monitor.setRecordedResult(newMonitorResult);
                    monitor.setStatisticsNum(null);
                    monitor.setStatisticsResult(null);
                    break;
                default:
                    monitor.setRealtimeResult(null);
                    monitor.setRealtimeNum(null);
                    monitor.setRecordedResult(null);
                    monitor.setRecordedNum(null);
                    monitor.setStatisticsNum(String.valueOf(num));
                    monitor.setStatisticsResult(newMonitorResult);
                    break;
            }
            monitor.setQualifiedNum(qualifiedNum);
            return this.updateById(monitor);
        } else {
            return true;
        }
    }


    @Override
    public Integer getDevCheckState(String devId) {
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", devId)
                .eq("pq_monitor.Check_Flag", 1);
        List<PqMonitor> monitorList = this.list(wrapper);
        if (CollUtil.isNotEmpty(monitorList)) {
            List<Integer> allNumList = monitorList.stream().map(PqMonitor::getNum).collect(Collectors.toList());
            List<Integer> checkedNumList = monitorList.stream().filter(obj -> ObjectUtil.isNotNull(obj.getResultType())).map(PqMonitor::getNum).collect(Collectors.toList());

            if (checkedNumList.containsAll(allNumList)) {
                return CheckStateEnum.CHECKED.getValue();
            }
        }
        return CheckStateEnum.UNCHECKED.getValue();
    }

    @Override
    public Integer getDevCheckResult(String devId) {
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", devId)
                .eq("pq_monitor.Check_Flag", 1);
        List<PqMonitor> monitorList = this.list(wrapper);

        List<Integer> allResultFlags = new ArrayList<>();
        for (PqMonitor monitor : monitorList) {
            String resultType = monitor.getResultType();
            if (StrUtil.isNotBlank(resultType)) {
                if (DataSourceEnum.REAL_DATA.getValue().equals(resultType)) {
                    //allResultFlags.add(monitor.getRealtimeResult());
                    if (monitor.getRealtimeResult() == 0) {
                        allResultFlags.add(2);
                    }
                    if (monitor.getRealtimeResult() == 1) {
                        allResultFlags.add(1);
                    }
                    if (monitor.getRealtimeResult() == 4) {
                        allResultFlags.add(4);
                    }
                } else if (DataSourceEnum.WAVE_DATA.getValue().equals(resultType)) {
                    //allResultFlags.add(monitor.getRecordedResult());
                    if (monitor.getRecordedResult() == 0) {
                        allResultFlags.add(2);
                    }
                    if (monitor.getRecordedResult() == 1) {
                        allResultFlags.add(1);
                    }
                    if (monitor.getRecordedResult() == 4) {
                        allResultFlags.add(4);
                    }
                } else {
                    //allResultFlags.add(monitor.getStatisticsResult());
                    if (monitor.getStatisticsResult() == 0) {
                        allResultFlags.add(2);
                    }
                    if (monitor.getStatisticsResult() == 1) {
                        allResultFlags.add(1);
                    }
                    if (monitor.getStatisticsResult() == 4) {
                        allResultFlags.add(4);
                    }
                }
            }
        }

        return DetectionDataServiceImpl.isResultFlag(allResultFlags);
    }

    @Override
    public boolean removeByDevId(String devId) {
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", devId);
        return this.remove(wrapper);
    }

    @Override
    public PqMonitor getByDevIdAndNum(String devId, int monitorNum) {
        QueryWrapper<PqMonitor> wrapper = new QueryWrapper<>();
        wrapper.eq("pq_monitor.Dev_Id", devId)
                .eq("pq_monitor.Num", monitorNum);
        List<PqMonitor> pqMonitors = this.list(wrapper);
        if (CollUtil.isNotEmpty(pqMonitors)) {
            return pqMonitors.get(0);
        }
        return null;
    }
}
