package com.njcn.gather.steady.checksquare.component;

import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendSeriesFieldBO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据校验指标值大小关系规则。
 */
@Component
@RequiredArgsConstructor
public class SteadyChecksquareValueOrderRuleComponent {

    private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final List<String> REQUIRED_STATS = Collections.unmodifiableList(Arrays.asList("MAX", "CP95", "AVG", "MIN"));
    private static final int ABNORMAL_THRESHOLD = 1;

    private final SteadyChecksquareInfluxQueryComponent influxQueryComponent;

    public SteadyChecksquareValueOrderRuleVO check(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
                                                   Integer harmonicOrder, LocalDateTime startTime,
                                                   LocalDateTime endTime, int intervalMinutes) {
        SteadyChecksquareValueOrderRuleVO result = new SteadyChecksquareValueOrderRuleVO();
        if (!supportValueOrderRule(indicator)) {
            return result;
        }
        for (String phase : indicator.getPhaseCodes()) {
            Map<String, Map<LocalDateTime, BigDecimal>> statValueMap = queryStatValueMap(lineId, indicator,
                    harmonicOrder, phase, startTime, endTime, intervalMinutes);
            appendAbnormalDetails(result, phase, harmonicOrder, statValueMap);
        }
        result.setAbnormalPointCount(result.getAbnormalDetails().size());
        result.setAbnormal(result.getAbnormalPointCount() > ABNORMAL_THRESHOLD);
        return result;
    }

    private boolean supportValueOrderRule(SteadyTrendIndicatorDefinitionBO indicator) {
        return indicator != null && indicator.getSupportStats() != null && indicator.getSupportStats().containsAll(REQUIRED_STATS);
    }

    private Map<String, Map<LocalDateTime, BigDecimal>> queryStatValueMap(String lineId,
                                                                          SteadyTrendIndicatorDefinitionBO indicator,
                                                                          Integer harmonicOrder, String phase,
                                                                          LocalDateTime startTime, LocalDateTime endTime,
                                                                          int intervalMinutes) {
        Map<String, Map<LocalDateTime, BigDecimal>> result = new LinkedHashMap<String, Map<LocalDateTime, BigDecimal>>();
        List<SteadyTrendResolvedFieldBO> fields = new ArrayList<SteadyTrendResolvedFieldBO>();
        for (String statType : REQUIRED_STATS) {
            SteadyTrendResolvedFieldBO field = buildResolvedField(lineId, indicator, harmonicOrder, phase, statType);
            fields.add(field);
        }
        Map<String, List<SteadyChecksquareValuePointBO>> fieldValueMap =
                influxQueryComponent.queryStatValuePointMap(fields, startTime, endTime, intervalMinutes);
        if (fieldValueMap == null) {
            fieldValueMap = Collections.emptyMap();
        }
        for (String statType : REQUIRED_STATS) {
            result.put(statType, toValueMap(fieldValueMap.get(statType)));
        }
        return result;
    }

    private void appendAbnormalDetails(SteadyChecksquareValueOrderRuleVO result, String phase, Integer harmonicOrder,
                                        Map<String, Map<LocalDateTime, BigDecimal>> statValueMap) {
        Map<LocalDateTime, BigDecimal> maxValues = statValueMap.get("MAX");
        Map<LocalDateTime, BigDecimal> cp95Values = statValueMap.get("CP95");
        Map<LocalDateTime, BigDecimal> avgValues = statValueMap.get("AVG");
        Map<LocalDateTime, BigDecimal> minValues = statValueMap.get("MIN");
        if (maxValues == null || cp95Values == null || avgValues == null || minValues == null) {
            return;
        }
        for (Map.Entry<LocalDateTime, BigDecimal> entry : maxValues.entrySet()) {
            LocalDateTime time = entry.getKey();
            BigDecimal maxValue = entry.getValue();
            BigDecimal cp95Value = cp95Values.get(time);
            BigDecimal avgValue = avgValues.get(time);
            BigDecimal minValue = minValues.get(time);
            // 缺少任一统计值时由缺数校验负责，不重复计入大小关系异常。
            if (maxValue == null || cp95Value == null || avgValue == null || minValue == null) {
                continue;
            }
            if (maxValue.compareTo(cp95Value) >= 0 && cp95Value.compareTo(avgValue) >= 0 && avgValue.compareTo(minValue) >= 0) {
                continue;
            }
            result.getAbnormalDetails().add(buildDetail(time, phase, harmonicOrder, maxValue, minValue, avgValue, cp95Value));
        }
    }

    private SteadyChecksquareValueOrderDetailVO buildDetail(LocalDateTime time, String phase, Integer harmonicOrder,
                                                            BigDecimal maxValue, BigDecimal minValue,
                                                            BigDecimal avgValue, BigDecimal cp95Value) {
        SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
        detail.setTime(OUTPUT_TIME_FORMATTER.format(time));
        detail.setPhase(phase);
        detail.setHarmonicOrder(harmonicOrder);
        detail.setMaxValue(maxValue);
        detail.setMinValue(minValue);
        detail.setAvgValue(avgValue);
        detail.setCp95Value(cp95Value);
        return detail;
    }

    private Map<LocalDateTime, BigDecimal> toValueMap(List<SteadyChecksquareValuePointBO> points) {
        Map<LocalDateTime, BigDecimal> result = new LinkedHashMap<LocalDateTime, BigDecimal>();
        if (points == null || points.isEmpty()) {
            return result;
        }
        for (SteadyChecksquareValuePointBO point : points) {
            if (point != null && point.getTime() != null && point.getValue() != null) {
                result.put(point.getTime(), point.getValue());
            }
        }
        return result;
    }

    private SteadyTrendResolvedFieldBO buildResolvedField(String lineId, SteadyTrendIndicatorDefinitionBO indicator,
                                                          Integer harmonicOrder, String phase, String statType) {
        SteadyTrendResolvedFieldBO field = new SteadyTrendResolvedFieldBO();
        field.setMeasurement(indicator.getTableName());
        field.setField(resolveField(indicator, harmonicOrder));
        field.setLineId(lineId);
        field.setIndicatorCode(indicator.getIndicatorCode());
        field.setIndicatorName(indicator.getName());
        field.setPhase(phase);
        field.setStatType(statType);
        field.setUnit(indicator.getUnit());
        return field;
    }

    private String resolveField(SteadyTrendIndicatorDefinitionBO indicator, Integer harmonicOrder) {
        if (Boolean.TRUE.equals(indicator.getHarmonic())) {
            return indicator.getHarmonicFieldPrefix() + "_" + harmonicOrder;
        }
        List<SteadyTrendSeriesFieldBO> fields = indicator.getSeriesFields();
        if (fields == null || fields.isEmpty()) {
            return "";
        }
        return fields.get(0).getField();
    }
}
