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

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.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njcn.common.pojo.enums.common.DataStateEnum;
import com.njcn.gather.device.pojo.vo.PqDevVO;
import com.njcn.gather.plan.mapper.PqDevCheckHistoryMapper;
import com.njcn.gather.plan.pojo.po.AdPlan;
import com.njcn.gather.plan.pojo.po.PqDevCheckHistory;
import com.njcn.gather.plan.pojo.vo.PlanStatisticsItemVO;
import com.njcn.gather.plan.service.IPqDevCheckHistoryService;
import com.njcn.gather.script.mapper.PqScriptDtlsMapper;
import com.njcn.gather.script.pojo.po.PqScriptDtls;
import com.njcn.gather.storage.pojo.po.SimAndDigBaseResult;
import com.njcn.gather.storage.service.SimAndDigHarmonicService;
import com.njcn.gather.storage.service.SimAndDigNonHarmonicService;
import com.njcn.gather.system.dictionary.pojo.po.DictTree;
import com.njcn.gather.system.dictionary.service.IDictTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class PqDevCheckHistoryServiceImpl extends ServiceImpl<PqDevCheckHistoryMapper, PqDevCheckHistory> implements IPqDevCheckHistoryService {

    private final SimAndDigNonHarmonicService adNonHarmonicService;
    private final SimAndDigHarmonicService adHarmonicService;
    private final PqScriptDtlsMapper pqScriptDtlsMapper;
    private final IDictTreeService dictTreeService;

    @Override
    @Transactional
    public void saveOrUpdateDeviceHistory(AdPlan plan, PqDevVO device) {
        if (ObjectUtil.isNull(plan)
                || ObjectUtil.isNull(device)
                || StrUtil.isBlank(plan.getScriptId())
                || ObjectUtil.isNull(plan.getCode())
                || StrUtil.isBlank(device.getId())
                || ObjectUtil.isNull(device.getRecheckNum())) {
            return;
        }

        Map<Integer, ScriptItemInfo> scriptItemInfoMap = getScriptItemInfoMap(plan.getScriptId());
        if (CollUtil.isEmpty(scriptItemInfoMap)) {
            return;
        }

        List<SimAndDigBaseResult> resultList = new ArrayList<>();
        resultList.addAll(adNonHarmonicService.listSimAndDigBaseResult(plan.getScriptId(), plan.getCode() + "", device.getId()));
        resultList.addAll(adHarmonicService.listAllResultData(plan.getScriptId(), plan.getCode() + "", device.getId()));
        if (CollUtil.isEmpty(resultList)) {
            return;
        }

        Map<String, List<SimAndDigBaseResult>> itemResultMap = resultList.stream()
                .filter(result -> ObjectUtil.isNotNull(result.getSort()))
                .map(result -> new AbstractMap.SimpleEntry<>(scriptItemInfoMap.get(result.getSort()), result))
                .filter(entry -> ObjectUtil.isNotNull(entry.getKey()))
                .collect(Collectors.groupingBy(
                        entry -> entry.getKey().getItemId(),
                        TreeMap::new,
                        Collectors.mapping(Map.Entry::getValue, Collectors.toList())
                ));

        LocalDateTime now = LocalDateTime.now();
        itemResultMap.forEach((itemId, results) -> {
            boolean hasUnqualified = results.stream().anyMatch(result -> isUnqualifiedResultFlag(result.getResultFlag()));
            boolean hasQualified = results.stream().anyMatch(result -> Objects.equals(result.getResultFlag(), 1));
            if (!hasUnqualified && !hasQualified) {
                return;
            }

            ScriptItemInfo itemInfo = scriptItemInfoMap.get(results.get(0).getSort());
            PqDevCheckHistory history = this.getOne(new LambdaQueryWrapper<PqDevCheckHistory>()
                    .eq(PqDevCheckHistory::getPlanId, plan.getId())
                    .eq(PqDevCheckHistory::getDevId, device.getId())
                    .eq(PqDevCheckHistory::getRecheckNum, device.getRecheckNum())
                    .eq(PqDevCheckHistory::getItemId, itemId)
                    .last("LIMIT 1"));

            if (ObjectUtil.isNull(history)) {
                history = new PqDevCheckHistory();
                history.setId(UUID.randomUUID().toString().replaceAll("-", ""));
                history.setPlanId(plan.getId());
                history.setDevId(device.getId());
                history.setRecheckNum(device.getRecheckNum());
                history.setItemId(itemId);
                history.setCreateBy(device.getCheckBy());
                history.setCreateTime(now);
            }

            history.setDevType(device.getDevType());
            history.setManufacturer(device.getManufacturer());
            history.setItemName(itemInfo.getItemName());
            history.setResult(hasUnqualified ? 0 : 1);
            history.setCheckTime(ObjectUtil.isNotNull(device.getCheckEndTime()) ? device.getCheckEndTime() : now);
            history.setState(DataStateEnum.ENABLE.getCode());
            history.setUpdateBy(device.getCheckBy());
            history.setUpdateTime(now);
            this.saveOrUpdate(history);
        });
    }

    @Override
    public void backfillPlanHistoryIfEmpty(AdPlan plan, List<PqDevVO> checkedDevices) {
        if (ObjectUtil.isNull(plan) || CollUtil.isEmpty(checkedDevices)) {
            return;
        }
        long historyCount = this.count(new LambdaQueryWrapper<PqDevCheckHistory>()
                .eq(PqDevCheckHistory::getPlanId, plan.getId())
                .eq(PqDevCheckHistory::getState, DataStateEnum.ENABLE.getCode()));
        if (historyCount > 0) {
            return;
        }
        checkedDevices.forEach(device -> saveOrUpdateDeviceHistory(plan, device));
    }

    @Override
    public List<PlanStatisticsItemVO> listItemDistributions(String planId, String manufacturer, String devType) {
        List<PlanStatisticsItemVO> itemDistributions = this.baseMapper.listItemDistributions(planId, manufacturer, devType);
        if (CollUtil.isEmpty(itemDistributions)) {
            return Collections.emptyList();
        }
        itemDistributions.forEach(item -> item.setUnqualifiedCount(defaultZero(item.getUnqualifiedCount())));
        return itemDistributions;
    }

    private Map<Integer, ScriptItemInfo> getScriptItemInfoMap(String scriptId) {
        List<PqScriptDtls> scriptDtlsList = pqScriptDtlsMapper.selectList(new LambdaQueryWrapper<PqScriptDtls>()
                .eq(PqScriptDtls::getScriptId, scriptId)
                .ne(PqScriptDtls::getScriptIndex, -1)
                .eq(PqScriptDtls::getEnable, DataStateEnum.ENABLE.getCode())
        );
        if (CollUtil.isEmpty(scriptDtlsList)) {
            return Collections.emptyMap();
        }

        List<String> scriptTypeIds = scriptDtlsList.stream()
                .map(PqScriptDtls::getScriptType)
                .filter(StrUtil::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
        Map<String, DictTree> dictTreeMap = Collections.emptyMap();
        if (CollUtil.isNotEmpty(scriptTypeIds)) {
            dictTreeMap = dictTreeService.getDictTreeById(scriptTypeIds).stream()
                    .collect(Collectors.toMap(DictTree::getId, dictTree -> dictTree, (left, right) -> left));
        }

        Map<String, DictTree> finalDictTreeMap = dictTreeMap;
        return scriptDtlsList.stream()
                .collect(Collectors.groupingBy(PqScriptDtls::getScriptIndex, TreeMap::new, Collectors.toList()))
                .entrySet()
                .stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> resolveScriptItemInfo(entry.getKey(), entry.getValue(), finalDictTreeMap),
                        (left, right) -> left,
                        TreeMap::new
                ));
    }

    private ScriptItemInfo resolveScriptItemInfo(Integer sort, List<PqScriptDtls> dtlsList, Map<String, DictTree> dictTreeMap) {
        if (CollUtil.isEmpty(dtlsList)) {
            return new ScriptItemInfo(String.valueOf(sort), "检测项" + sort);
        }
        PqScriptDtls first = dtlsList.get(0);
        DictTree dictTree = dictTreeMap.get(first.getScriptType());
        if (ObjectUtil.isNotNull(dictTree) && StrUtil.isNotBlank(dictTree.getName())) {
            return new ScriptItemInfo(dictTree.getId(), dictTree.getName());
        }
        if (StrUtil.isNotBlank(first.getScriptType())) {
            return new ScriptItemInfo(first.getScriptType(), "检测项" + sort);
        }
        return new ScriptItemInfo(String.valueOf(sort), "检测项" + sort);
    }

    private boolean isUnqualifiedResultFlag(Integer resultFlag) {
        return ObjectUtil.isNotNull(resultFlag)
                && !Objects.equals(resultFlag, 1)
                && !Objects.equals(resultFlag, 4)
                && !Objects.equals(resultFlag, 5);
    }

    private Integer defaultZero(Integer value) {
        return ObjectUtil.isNull(value) ? 0 : value;
    }

    private static class ScriptItemInfo {
        private final String itemId;
        private final String itemName;

        private ScriptItemInfo(String itemId, String itemName) {
            this.itemId = itemId;
            this.itemName = itemName;
        }

        private String getItemId() {
            return itemId;
        }

        private String getItemName() {
            return itemName;
        }
    }
}
