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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.njcn.db.mybatisplus.handler.DynamicTableNameHandler;
import com.njcn.gather.storage.mapper.SimAndDigHarmonicMappper;
import com.njcn.gather.storage.mapper.SimAndDigNonHarmonicMapper;
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
import com.njcn.gather.storage.pojo.param.StorageParam;
import com.njcn.gather.storage.pojo.po.ContrastHarmonicResult;
import com.njcn.gather.storage.pojo.po.SimAndDigBaseResult;
import com.njcn.gather.storage.pojo.po.SimAndDigHarmonicResult;
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
import com.njcn.gather.storage.pojo.vo.RawDataVO;
import com.njcn.gather.storage.pojo.vo.RawResultDataVO;
import com.njcn.gather.storage.service.SimAndDigHarmonicService;
import com.njcn.gather.system.dictionary.mapper.DictTreeMapper;
import com.njcn.gather.system.dictionary.pojo.enums.DictDataEnum;
import com.njcn.gather.system.dictionary.pojo.po.DictTree;
import com.njcn.gather.system.dictionary.service.IDictTreeService;
import com.njcn.gather.util.StorageUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author caozehui
 * @data 2024-12-30
 */
@Service
@RequiredArgsConstructor
public class SimAndDigHarmonicServiceImpl extends ServiceImpl<SimAndDigHarmonicMappper, SimAndDigHarmonicResult> implements SimAndDigHarmonicService {

    private final SimAndDigNonHarmonicMapper adNonHarmonicMapper;
    private final DictTreeMapper dictTreeMapper;
    private final IDictTreeService dictTreeService;

    @Override
    public List<SimAndDigBaseResult> get(String scriptId, List<Integer> sort, String deviceId, String chnNum, String code) {
        String prefix = "ad_harmonic_result_";
        DynamicTableNameHandler.setTableName(prefix + code);
        MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
        String monitorId = deviceId + "_" + chnNum;
        if ("-1".equals(chnNum)) {
            monitorId = deviceId;
        }
        wrapper.like(SimAndDigHarmonicResult::getDevMonitorId, monitorId)
                .eq(ObjectUtil.isNotNull(scriptId), SimAndDigHarmonicResult::getScriptId, scriptId)
                .in(CollUtil.isNotEmpty(sort), SimAndDigHarmonicResult::getSort, sort);
        List<SimAndDigHarmonicResult> list = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
        DynamicTableNameHandler.remove();
        return BeanUtil.copyToList(list, SimAndDigBaseResult.class);
    }

    @Override
    public Map<String, List<RawDataVO>> listHarmData(StorageParam param) {
        String prefix = "ad_harmonic_";
        DynamicTableNameHandler.setTableName(prefix + param.getCode());
        MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
        wrapper.like(SimAndDigHarmonicResult::getDevMonitorId, param.getDevId() + "_" + param.getDevNum())
                .eq(ObjectUtil.isNotNull(param.getScriptId()), SimAndDigHarmonicResult::getScriptId, param.getScriptId())
                .in(ObjectUtil.isNotEmpty(param.getIndex()), SimAndDigHarmonicResult::getSort, param.getIndex())
                .orderByAsc(SimAndDigHarmonicResult::getTimeId)
        ;
        List<SimAndDigHarmonicResult> simAndDigHarmonicResults = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
        LinkedHashMap<String, List<SimAndDigHarmonicResult>> adMap =
                simAndDigHarmonicResults.stream().collect(Collectors.groupingBy(SimAndDigHarmonicResult::getAdType, LinkedHashMap::new, Collectors.toList()));
        List<DictTree> dictTreeById = dictTreeService.getDictTreeById(new ArrayList<>(adMap.keySet()));
        Map<String, DictTree> dictTreeByName = dictTreeById.stream().collect(Collectors.toMap(DictTree::getId, Function.identity()));
        Map<String, List<RawDataVO>> info = new LinkedHashMap<>(3);
        adMap.forEach((ad, value) -> {
            if (dictTreeByName.containsKey(ad)) {
                DictTree dictData = dictTreeByName.get(ad);
                List<RawDataVO> rawDataVOS = new ArrayList<>();
                List<Double> harmNum = param.getHarmNum();
                RawDataVO dataVO;
                String unit;
                if (DictDataEnum.I2_50.getCode().equals(dictData.getCode()) || DictDataEnum.SI_1_49.getCode().equals(dictData.getCode())) {
                    unit = "A";
                } else if (DictDataEnum.P2_50.getCode().equals(dictData.getCode())) {
                    unit = "W";
                } else {
                    unit = "%";
                }
                for (SimAndDigHarmonicResult harmonicResult : value) {
                    for (Double i : harmNum) {
                        dataVO = new RawDataVO();
                        dataVO.setHarmNum(i);
                        dataVO.setUnit(unit);
                        try {
                            Field timeId = harmonicResult.getClass().getSuperclass().getSuperclass().getDeclaredField("timeId");
                            timeId.setAccessible(true);
                            LocalDateTime localDateTime = (LocalDateTime) timeId.get(harmonicResult);
                            dataVO.setTime(localDateTime.format(DateTimeFormatter.ofPattern(DatePattern.ISO8601_PATTERN)));

                            Field fieldA = harmonicResult.getClass().getDeclaredField("aValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldA.setAccessible(true);
                            dataVO.setDataA(StrUtil.isNotBlank(fieldA.get(harmonicResult) + "") ? new BigDecimal(fieldA.get(harmonicResult) + "").toPlainString() : null);

                            Field fieldB = harmonicResult.getClass().getDeclaredField("bValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldB.setAccessible(true);
                            dataVO.setDataB(StrUtil.isNotBlank(fieldB.get(harmonicResult) + "") ? new BigDecimal(fieldB.get(harmonicResult) + "").toPlainString() : null);

                            Field fieldC = harmonicResult.getClass().getDeclaredField("cValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldC.setAccessible(true);
                            dataVO.setDataC(StrUtil.isNotBlank(fieldC.get(harmonicResult) + "") ? new BigDecimal(fieldC.get(harmonicResult) + "").toPlainString() : null);
                        } catch (NoSuchFieldException e) {
                            throw new RuntimeException(e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                        rawDataVOS.add(dataVO);
                    }
                }
                info.put(dictData.getName(), rawDataVOS);
            }
        });
        DynamicTableNameHandler.remove();
        return info;
    }

    @Override
    public Map<String, List<RawResultDataVO>> listHarmResultData(StorageParam param) {
        String prefix = "ad_harmonic_result_";
        DynamicTableNameHandler.setTableName(prefix + param.getCode());
        MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
        wrapper.like(SimAndDigHarmonicResult::getDevMonitorId, param.getDevId() + "_" + param.getDevNum())
                .eq(ObjectUtil.isNotNull(param.getScriptId()), SimAndDigHarmonicResult::getScriptId, param.getScriptId())
                .in(ObjectUtil.isNotEmpty(param.getIndex()), SimAndDigHarmonicResult::getSort, param.getIndex())
                .orderByAsc(SimAndDigHarmonicResult::getTimeId);
        List<SimAndDigHarmonicResult> adHarmonicResults = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
        LinkedHashMap<String, List<SimAndDigHarmonicResult>> adMap =
                adHarmonicResults.stream().collect(Collectors.groupingBy(SimAndDigHarmonicResult::getAdType, LinkedHashMap::new, Collectors.toList()));
        List<DictTree> dictTreeById = dictTreeService.getDictTreeById(new ArrayList<>(adMap.keySet()));
        Map<String, DictTree> dictTreeByName = dictTreeById.stream().collect(Collectors.toMap(DictTree::getId, Function.identity()));
        Map<String, List<RawResultDataVO>> info = new LinkedHashMap<>(3);
        adMap.forEach((ad, value) -> {
            if (dictTreeByName.containsKey(ad)) {
                DictTree dictData = dictTreeByName.get(ad);
                List<RawResultDataVO> rawDataVOS = new ArrayList<>();
                List<Double> harmNum = param.getHarmNum();
                RawResultDataVO dataVO;
                String unit;
                if (DictDataEnum.I2_50.getCode().equals(dictData.getCode()) || DictDataEnum.SI_1_49.getCode().equals(dictData.getCode())) {
                    unit = "A";
                } else if (DictDataEnum.P2_50.getCode().equals(dictData.getCode())) {
                    unit = "W";
                } else {
                    unit = "%";
                }
                for (SimAndDigHarmonicResult harmonicResult : value) {
                    for (Double i : harmNum) {
                        dataVO = new RawResultDataVO();
                        dataVO.setHarmNum(i);
                        dataVO.setUnit(unit);
                        try {
                            Field fieldA = harmonicResult.getClass().getDeclaredField("aValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldA.setAccessible(true);
                            RawResultDataVO.DetectionData a = JSON.parseObject(fieldA.get(harmonicResult) + "", RawResultDataVO.DetectionData.class);
                            dataVO.setDataA(a);

                            Field fieldB = harmonicResult.getClass().getDeclaredField("bValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldB.setAccessible(true);
                            RawResultDataVO.DetectionData b = JSON.parseObject(fieldB.get(harmonicResult) + "", RawResultDataVO.DetectionData.class);
                            dataVO.setDataB(b);

                            Field fieldC = harmonicResult.getClass().getDeclaredField("cValue" + StorageUtil.isHarmOrInHarm(i).intValue());
                            fieldC.setAccessible(true);
                            RawResultDataVO.DetectionData c = JSON.parseObject(fieldC.get(harmonicResult) + "", RawResultDataVO.DetectionData.class);
                            dataVO.setDataC(c);
                            if (ObjectUtil.isNotNull(a)) {
                                dataVO.setRadius(a.getRadius());
                            }
                            dataVO.setIsData(StorageUtil.setResultFlag(Arrays.asList(a, b, c)));
                        } catch (NoSuchFieldException e) {
                            throw new RuntimeException(e);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                        rawDataVOS.add(dataVO);
                    }
                }
                info.put(dictData.getName(), rawDataVOS);
            }
        });
        DynamicTableNameHandler.remove();
        return info;
    }

    @Override
    public List<Integer> getIndex(StorageParam param) {
        String prefix = "ad_harmonic_result_";
        String prefixNon = "ad_non_harmonic_result_";
        DynamicTableNameHandler.setTableName(prefix + param.getCode());
        LambdaQueryWrapper<SimAndDigHarmonicResult> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.select(SimAndDigHarmonicResult::getSort)
                .likeRight(StrUtil.isNotBlank(param.getDevId()), SimAndDigHarmonicResult::getDevMonitorId, param.getDevId())
                .ne(SimAndDigHarmonicResult::getResultFlag, 1)
                .ne(SimAndDigHarmonicResult::getResultFlag, 4)
                .ne(SimAndDigHarmonicResult::getResultFlag, 5)
                .eq(SimAndDigHarmonicResult::getScriptId, param.getScriptId());
        List<SimAndDigHarmonicResult> adHarmonicResultList = this.list(lambdaQueryWrapper);
        List<Integer> indexes = new ArrayList<>(adHarmonicResultList.stream().map(SimAndDigHarmonicResult::getSort).distinct().collect(Collectors.toList()));

        DynamicTableNameHandler.setTableName(prefixNon + param.getCode());
        LambdaQueryWrapper<SimAndDigNonHarmonicResult> resultLambdaQueryWrapper = new LambdaQueryWrapper<>();
        resultLambdaQueryWrapper.select(SimAndDigNonHarmonicResult::getSort)
                .likeRight(StrUtil.isNotBlank(param.getDevId()), SimAndDigNonHarmonicResult::getDevMonitorId, param.getDevId())
                .ne(SimAndDigNonHarmonicResult::getResultFlag, 1)
                .ne(SimAndDigNonHarmonicResult::getResultFlag, 4)
                .ne(SimAndDigNonHarmonicResult::getResultFlag, 5)
                .eq(SimAndDigNonHarmonicResult::getScriptId, param.getScriptId());
        List<SimAndDigNonHarmonicResult> nonHarmonicResults = adNonHarmonicMapper.selectList(resultLambdaQueryWrapper);
        indexes.addAll(nonHarmonicResults.stream().map(SimAndDigNonHarmonicResult::getSort).distinct().collect(Collectors.toList()));
        DynamicTableNameHandler.remove();
        return indexes;
    }

    @Override
    public SimAndDigHarmonicResult getSingleResult(SingleNonHarmParam singleNonHarmParam) {
        if (ObjectUtil.isNotNull(singleNonHarmParam)) {
            String prefix = "ad_harmonic_result_";
            DynamicTableNameHandler.setTableName(prefix + singleNonHarmParam.getPlanCode());
            MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
            wrapper.like(SimAndDigHarmonicResult::getDevMonitorId, singleNonHarmParam.getDevId() + "_" + singleNonHarmParam.getChannelNo())
                    .eq(SimAndDigHarmonicResult::getSort, singleNonHarmParam.getSort())
                    .eq(SimAndDigHarmonicResult::getAdType, singleNonHarmParam.getAdType());
            List<SimAndDigHarmonicResult> adHarmonicResults = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
            DynamicTableNameHandler.remove();
            if (CollectionUtil.isNotEmpty(adHarmonicResults)) {
                return adHarmonicResults.get(0);
            }
        }
        return null;
    }

    @Override
    public List<SimAndDigHarmonicResult> listAllResultData(String scriptId, String code, String devId) {
        String prefix = "ad_harmonic_result_" + code;
        DynamicTableNameHandler.setTableName(prefix);
        MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(SimAndDigHarmonicResult.class)
                .selectAs(DictTree::getCode, SimAndDigHarmonicResult::getAdType)
                .leftJoin(DictTree.class, DictTree::getId, SimAndDigHarmonicResult::getAdType).eq(SimAndDigHarmonicResult::getScriptId, scriptId)
                .likeRight(SimAndDigHarmonicResult::getDevMonitorId, devId);
        List<SimAndDigHarmonicResult> results = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
        DynamicTableNameHandler.remove();
        return results;
    }

    @Override
    public List<SimAndDigHarmonicResult> listAllRawData(String scriptId, String code, String devId) {
        String prefix = "ad_harmonic_" + code;
        DynamicTableNameHandler.setTableName(prefix);
        MPJLambdaWrapper<SimAndDigHarmonicResult> wrapper = new MPJLambdaWrapper<>();
        wrapper.selectAll(SimAndDigHarmonicResult.class)
                .selectAs(DictTree::getCode, SimAndDigHarmonicResult::getAdType)
                .leftJoin(DictTree.class, DictTree::getId, SimAndDigHarmonicResult::getAdType).eq(SimAndDigHarmonicResult::getScriptId, scriptId)
                .likeRight(SimAndDigHarmonicResult::getDevMonitorId, devId);
        List<SimAndDigHarmonicResult> results = this.getBaseMapper().selectJoinList(SimAndDigHarmonicResult.class, wrapper);
        DynamicTableNameHandler.remove();
        return results;
    }

    @Override
    public Map<String, Integer> maxSortByDevIds(String code, String scriptId, List<String> devIds) {
        Map<String, Integer> result = new HashMap<>();
        if (CollUtil.isEmpty(devIds)) {
            return result;
        }
        DynamicTableNameHandler.setTableName("ad_harmonic_result_" + code);
        try {
            LambdaQueryWrapper<SimAndDigHarmonicResult> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(SimAndDigHarmonicResult::getDevMonitorId, SimAndDigHarmonicResult::getSort)
                    .eq(SimAndDigHarmonicResult::getScriptId, scriptId)
                    .and(w -> {
                        for (String devId : devIds) {
                            w.or().likeRight(SimAndDigHarmonicResult::getDevMonitorId, devId + "_");
                        }
                    });
            this.list(wrapper).forEach(row -> {
                String devId = row.getDevMonitorId().split("_")[0];
                result.merge(devId, row.getSort(), Math::max);
            });
            return result;
        } finally {
            DynamicTableNameHandler.remove();
        }
    }

    @Override
    public List<SimAndDigHarmonicResult> listByDevIdsAndSorts(String code, String scriptId, List<String> devIds, List<Integer> sorts) {
        if (CollUtil.isEmpty(devIds) || CollUtil.isEmpty(sorts)) {
            return Collections.emptyList();
        }
        DynamicTableNameHandler.setTableName("ad_harmonic_result_" + code);
        try {
            LambdaQueryWrapper<SimAndDigHarmonicResult> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(SimAndDigHarmonicResult::getScriptId, scriptId)
                    .in(SimAndDigHarmonicResult::getSort, sorts)
                    .and(w -> {
                        for (String devId : devIds) {
                            w.or().likeRight(SimAndDigHarmonicResult::getDevMonitorId, devId + "_");
                        }
                    });
            return this.list(wrapper);
        } finally {
            DynamicTableNameHandler.remove();
        }
    }

    @Override
    public Map<String, List<Integer>> resultFlagsByDevIdsAndSorts(String code, String scriptId, List<String> devIds, List<Integer> sorts) {
        Map<String, List<Integer>> result = new HashMap<>();
        if (CollUtil.isEmpty(devIds) || StrUtil.isBlank(code)) {
            return result;
        }
        if (sorts != null && CollUtil.isEmpty(sorts)) {
            return result;
        }
        DynamicTableNameHandler.setTableName("ad_harmonic_result_" + code);
        try {
            LambdaQueryWrapper<SimAndDigHarmonicResult> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(SimAndDigHarmonicResult::getDevMonitorId,
                            SimAndDigHarmonicResult::getResultFlag)
                    .eq(StrUtil.isNotBlank(scriptId), SimAndDigHarmonicResult::getScriptId, scriptId)
                    .in(CollUtil.isNotEmpty(sorts), SimAndDigHarmonicResult::getSort, sorts)
                    .and(w -> {
                        for (String devId : devIds) {
                            w.or().likeRight(SimAndDigHarmonicResult::getDevMonitorId, devId + "_");
                        }
                    });
            this.list(wrapper).forEach(row -> {
                String devId = resolveDevId(row.getDevMonitorId(), devIds);
                if (StrUtil.isNotBlank(devId) && row.getResultFlag() != null) {
                    result.computeIfAbsent(devId, key -> new ArrayList<>()).add(row.getResultFlag());
                }
            });
            return result;
        } finally {
            DynamicTableNameHandler.remove();
        }
    }

    private String resolveDevId(String devMonitorId, List<String> devIds) {
        if (StrUtil.isBlank(devMonitorId)) {
            return null;
        }
        for (String devId : devIds) {
            if (StrUtil.isNotBlank(devId) && devMonitorId.startsWith(devId + "_")) {
                return devId;
            }
        }
        return null;
    }
}
