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

import cn.hutool.core.collection.CollUtil;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.detection.handler.SocketDevResponseService;
import com.njcn.gather.detection.pojo.param.FormalProgressParam;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.pojo.vo.FormalProgressVO;
import com.njcn.gather.detection.service.FormalProgressService;
import com.njcn.gather.detection.util.socket.websocket.WebServiceManager;
import com.njcn.gather.device.pojo.enums.CommonEnum;
import com.njcn.gather.device.pojo.vo.PreDetection;
import com.njcn.gather.device.service.IPqDevService;
import com.njcn.gather.script.pojo.param.PqScriptIssueParam;
import com.njcn.gather.script.pojo.po.SourceIssue;
import com.njcn.gather.script.service.IPqScriptDtlsService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class FormalProgressServiceImpl implements FormalProgressService {

    private static final long RESUME_RELEASE_DELAY_MILLIS = 300L;

    private final IPqDevService pqDevService;
    private final IPqScriptDtlsService pqScriptDtlsService;
    private final IDictTreeService dictTreeService;
    private final SimAndDigHarmonicService harmonicService;
    private final SimAndDigNonHarmonicService nonHarmonicService;
    private final SocketDevResponseService socketDevResponseService;

    @Override
    public FormalProgressVO restoreAndRelease(FormalProgressParam param) {
        PreDetectionParam preParam = WebServiceManager.getPreDetectionParam(param.getUserPageId());
        if (preParam == null) {
            throw new BusinessException(CommonResponseEnum.FAIL, "未找到续检上下文，请重新开始检测");
        }
        FormalProgressVO vo = buildSnapshot(preParam, param.getDevIds());
        releaseResumeAfterSnapshotApplied(preParam, vo.getNextSort());
        return vo;
    }

    @Override
    public Integer calculateNextSort(PreDetectionParam param) {
        return buildSnapshot(param, param.getDevIds()).getNextSort();
    }

    private FormalProgressVO buildSnapshot(PreDetectionParam param, List<String> devIds) {
        FormalProgressVO vo = new FormalProgressVO();
        vo.setFormalCheckTime(pqDevService.getMinFormalCheckTime(devIds));

        Map<String, Integer> harmonicMax = harmonicService.maxSortByDevIds(param.getCode(), param.getScriptId(), devIds);
        Map<String, Integer> nonHarmonicMax = nonHarmonicService.maxSortByDevIds(param.getCode(), param.getScriptId(), devIds);

        Integer minMaxSort = devIds.stream()
                .map(devId -> Math.max(harmonicMax.getOrDefault(devId, -1), nonHarmonicMax.getOrDefault(devId, -1)))
                .min(Integer::compareTo)
                .orElse(-1);
        vo.setMinMaxSort(minMaxSort);

        List<SourceIssue> allIssues = buildAllFormalIssues(param);
        List<Integer> allSorts = allIssues.stream()
                .map(SourceIssue::getIndex)
                .filter(sort -> sort != null && sort >= 0)
                .sorted()
                .collect(Collectors.toList());
        Integer nextSort = allSorts.stream()
                .filter(sort -> sort > minMaxSort)
                .findFirst()
                .orElse(null);
        vo.setNextSort(nextSort);

        Set<String> completedTypes = completedBigTypes(allIssues, minMaxSort);
        long totalBigTypes = allIssues.stream().map(SourceIssue::getType).distinct().count();
        vo.setProcess(totalBigTypes == 0 ? 0D : completedTypes.size() * 100D / totalBigTypes);
        vo.setTableRows(buildRows(param, devIds, allIssues, completedTypes));
        return vo;
    }

    private void releaseResumeAfterSnapshotApplied(PreDetectionParam preParam, Integer nextSort) {
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(RESUME_RELEASE_DELAY_MILLIS);
                socketDevResponseService.startFormalAfterProgressRestore(preParam, nextSort);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                log.warn("正式检测续检放行等待被中断", e);
            } catch (Exception e) {
                log.error("正式检测续检放行失败", e);
            }
        });
    }

    private List<SourceIssue> buildAllFormalIssues(PreDetectionParam param) {
        PqScriptIssueParam issueParam = new PqScriptIssueParam();
        issueParam.setPlanId(param.getPlanId());
        issueParam.setSourceId(param.getSourceName());
        issueParam.setDevIds(param.getDevIds());
        issueParam.setScriptId(param.getScriptId());
        issueParam.setIsPhaseSequence(CommonEnum.FORMAL_TEST.getValue());
        return pqScriptDtlsService.listSourceIssue(issueParam)
                .stream()
                .sorted(Comparator.comparing(SourceIssue::getIndex))
                .collect(Collectors.toList());
    }

    private Set<String> completedBigTypes(List<SourceIssue> issues, Integer minMaxSort) {
        return issues.stream()
                .collect(Collectors.groupingBy(SourceIssue::getType, LinkedHashMap::new, Collectors.toList()))
                .entrySet()
                .stream()
                .filter(entry -> entry.getValue().stream().allMatch(issue -> issue.getIndex() <= minMaxSort))
                .map(Map.Entry::getKey)
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    private List<FormalProgressVO.TableRow> buildRows(PreDetectionParam param,
                                                      List<String> devIds,
                                                      List<SourceIssue> issues,
                                                      Set<String> completedTypes) {
        Map<String, List<SourceIssue>> byType = issues.stream()
                .collect(Collectors.groupingBy(SourceIssue::getType, LinkedHashMap::new, Collectors.toList()));
        if (byType.isEmpty()) {
            return Collections.emptyList();
        }
        Map<String, DictTree> dictTreeByCode = dictTreeService.lambdaQuery()
                .in(DictTree::getCode, byType.keySet())
                .list()
                .stream()
                .collect(Collectors.toMap(DictTree::getCode, dictTree -> dictTree, (left, right) -> left));
        return byType.entrySet().stream().map(entry -> {
            List<SourceIssue> typeIssues = entry.getValue();
            boolean completed = completedTypes.contains(entry.getKey());
            DictTree dictTree = dictTreeByCode.get(entry.getKey());
            FormalProgressVO.TableRow row = new FormalProgressVO.TableRow();
            row.setScriptCode(entry.getKey());
            row.setScriptName(dictTree.getName());
            row.setScriptType(dictTree.getId());
            row.setDevices(completed
                    ? buildCompletedDeviceResults(param, devIds, typeIssues)
                    : buildUnknownDeviceResults(devIds));
            return row;
        }).collect(Collectors.toList());
    }

    private List<FormalProgressVO.DeviceResult> buildUnknownDeviceResults(List<String> devIds) {
        return pqDevService.getDevInfo(devIds).stream().map(dev -> {
            FormalProgressVO.DeviceResult item = new FormalProgressVO.DeviceResult();
            item.setDeviceId(dev.getDevId());
            item.setDeviceName(dev.getDevName());
            item.setChnResult(dev.getMonitorList().stream().map(monitor -> -1).collect(Collectors.toList()));
            return item;
        }).collect(Collectors.toList());
    }

    private List<FormalProgressVO.DeviceResult> buildCompletedDeviceResults(PreDetectionParam param,
                                                                            List<String> devIds,
                                                                            List<SourceIssue> typeIssues) {
        List<Integer> sorts = typeIssues.stream().map(SourceIssue::getIndex).distinct().collect(Collectors.toList());
        List<SimAndDigBaseResult> rows = new ArrayList<>();
        rows.addAll(harmonicService.listByDevIdsAndSorts(param.getCode(), param.getScriptId(), devIds, sorts));
        rows.addAll(nonHarmonicService.listByDevIdsAndSorts(param.getCode(), param.getScriptId(), devIds, sorts));

        Map<String, List<Integer>> flagMap = rows.stream()
                .filter(row -> row.getResultFlag() != null)
                .collect(Collectors.groupingBy(SimAndDigBaseResult::getDevMonitorId,
                        Collectors.mapping(SimAndDigBaseResult::getResultFlag, Collectors.toList())));

        return pqDevService.getDevInfo(devIds).stream().map(dev -> {
            FormalProgressVO.DeviceResult item = new FormalProgressVO.DeviceResult();
            item.setDeviceId(dev.getDevId());
            item.setDeviceName(dev.getDevName());
            item.setChnResult(dev.getMonitorList().stream()
                    .map(monitor -> mergeResultFlags(resolveResultFlags(flagMap, dev, monitor)))
                    .collect(Collectors.toList()));
            return item;
        }).collect(Collectors.toList());
    }

    private List<Integer> resolveResultFlags(Map<String, List<Integer>> flagMap, PreDetection dev, PreDetection.MonitorListDTO monitor) {
        List<Integer> flags = new ArrayList<>();
        List<Integer> byDevLine = flagMap.get(dev.getDevId() + "_" + monitor.getLine());
        if (CollUtil.isNotEmpty(byDevLine)) {
            flags.addAll(byDevLine);
        }
        List<Integer> byLineId = flagMap.get(monitor.getLineId());
        if (CollUtil.isNotEmpty(byLineId)) {
            flags.addAll(byLineId);
        }
        return flags;
    }

    private Integer mergeResultFlags(List<Integer> flags) {
        if (CollUtil.isEmpty(flags)) {
            return -1;
        }
        List<Integer> effective = flags.stream()
                .filter(flag -> flag != null && flag != 4 && flag != 5)
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(effective)) {
            return effective.stream().max(Integer::compareTo).orElse(1);
        }
        return flags.get(0);
    }
}
