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

import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
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.SteadyTrendSeriesFieldBO;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
 * 数据校验指标值大小关系规则测试。
 */
class SteadyChecksquareValueOrderRuleComponentTest {

    @Test
    void shouldMarkIndicatorAbnormalWhenInvalidPointCountGreaterThanOne() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        LocalDateTime firstTime = LocalDateTime.of(2026, 5, 1, 0, 0);
        LocalDateTime secondTime = LocalDateTime.of(2026, 5, 1, 0, 1);
        when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(statPointMap(
                        Arrays.asList(point(firstTime, "8"), point(secondTime, "9")),
                        Arrays.asList(point(firstTime, "9"), point(secondTime, "10")),
                        Arrays.asList(point(firstTime, "7"), point(secondTime, "8")),
                        Arrays.asList(point(firstTime, "1"), point(secondTime, "9"))));

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 2), 1);

        Assertions.assertEquals(Integer.valueOf(2), result.getAbnormalPointCount());
        Assertions.assertEquals(Boolean.TRUE, result.getAbnormal());
        Assertions.assertEquals(2, result.getAbnormalDetails().size());
        Assertions.assertEquals("2026-05-01 00:00:00", result.getAbnormalDetails().get(0).getTime());
        Assertions.assertEquals("A", result.getAbnormalDetails().get(0).getPhase());
        Assertions.assertEquals(new BigDecimal("8"), result.getAbnormalDetails().get(0).getMaxValue());
        Assertions.assertEquals(new BigDecimal("1"), result.getAbnormalDetails().get(0).getMinValue());
        Assertions.assertEquals(new BigDecimal("7"), result.getAbnormalDetails().get(0).getAvgValue());
        Assertions.assertEquals(new BigDecimal("9"), result.getAbnormalDetails().get(0).getCp95Value());
    }

    @Test
    void shouldTreatEqualAdjacentStatValuesAsNormal() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
        when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(statPointMap(
                        Collections.singletonList(point(time, "10")),
                        Collections.singletonList(point(time, "10")),
                        Collections.singletonList(point(time, "8")),
                        Collections.singletonList(point(time, "8"))));

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);

        Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
        Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
        Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
    }

    @Test
    void shouldNotMarkIndicatorAbnormalWhenOnlyOneInvalidPointExists() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
        when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(statPointMap(
                        Collections.singletonList(point(time, "8")),
                        Collections.singletonList(point(time, "10")),
                        Collections.singletonList(point(time, "8")),
                        Collections.singletonList(point(time, "1"))));

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);

        Assertions.assertEquals(Integer.valueOf(1), result.getAbnormalPointCount());
        Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
        Assertions.assertEquals(1, result.getAbnormalDetails().size());
    }

    @Test
    void shouldFillHarmonicOrderInAbnormalDetailForHarmonicIndicator() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
        when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(statPointMap(
                        Collections.singletonList(point(time, "8")),
                        Collections.singletonList(point(time, "10")),
                        Collections.singletonList(point(time, "8")),
                        Collections.singletonList(point(time, "1"))));
        SteadyTrendIndicatorDefinitionBO indicator = indicator();
        indicator.setHarmonic(true);
        indicator.setHarmonicFieldPrefix("v");

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator, 5,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);

        Assertions.assertEquals(1, result.getAbnormalDetails().size());
        Assertions.assertEquals(Integer.valueOf(5), result.getAbnormalDetails().get(0).getHarmonicOrder());
    }

    @Test
    void shouldSkipPointWhenAnyRequiredStatValueMissing() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        LocalDateTime time = LocalDateTime.of(2026, 5, 1, 0, 0);
        when(influxQueryComponent.queryStatValuePointMap(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(statPointMap(
                        Collections.singletonList(point(time, "10")),
                        Collections.singletonList(point(time, "11")),
                        Collections.emptyList(),
                        Collections.singletonList(point(time, "1"))));

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator(), null,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);

        Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
        Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
        Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
    }

    @Test
    void shouldSkipIndicatorWhenNotAllFourStatsSupported() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent component = new SteadyChecksquareValueOrderRuleComponent(influxQueryComponent);
        SteadyTrendIndicatorDefinitionBO indicator = indicator();
        indicator.setSupportStats(Collections.singletonList("AVG"));

        SteadyChecksquareValueOrderRuleVO result = component.check("line-001", indicator, null,
                LocalDateTime.of(2026, 5, 1, 0, 0), LocalDateTime.of(2026, 5, 1, 0, 1), 1);

        Assertions.assertEquals(Integer.valueOf(0), result.getAbnormalPointCount());
        Assertions.assertEquals(Boolean.FALSE, result.getAbnormal());
        Assertions.assertTrue(result.getAbnormalDetails().isEmpty());
    }

    private SteadyTrendIndicatorDefinitionBO indicator() {
        SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO();
        indicator.setIndicatorCode("V_RMS");
        indicator.setName("相电压有效值");
        indicator.setTableName("data_v");
        indicator.setPhaseCodes(Collections.singletonList("A"));
        indicator.setSeriesFields(Collections.singletonList(new SteadyTrendSeriesFieldBO("rms", "相电压有效值")));
        indicator.setSupportStats(Arrays.asList("AVG", "MAX", "MIN", "CP95"));
        indicator.setUnit("V");
        return indicator;
    }

    private SteadyChecksquareValuePointBO point(LocalDateTime time, String value) {
        SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
        point.setTime(time);
        point.setValue(new BigDecimal(value));
        return point;
    }

    private Map<String, List<SteadyChecksquareValuePointBO>> statPointMap(List<SteadyChecksquareValuePointBO> maxPoints,
                                                                          List<SteadyChecksquareValuePointBO> cp95Points,
                                                                          List<SteadyChecksquareValuePointBO> avgPoints,
                                                                          List<SteadyChecksquareValuePointBO> minPoints) {
        Map<String, List<SteadyChecksquareValuePointBO>> result =
                new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
        result.put("MAX", maxPoints);
        result.put("CP95", cp95Points);
        result.put("AVG", avgPoints);
        result.put("MIN", minPoints);
        return result;
    }
}
