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

import com.njcn.gather.report.pojo.enums.ItemReportKeyEnum;
import com.njcn.gather.report.pojo.result.SingleTestResult;
import com.njcn.gather.script.pojo.po.PqScriptCheckData;
import com.njcn.gather.script.pojo.vo.PqScriptDtlDataVO;
import com.njcn.gather.script.service.IPqScriptCheckDataService;
import com.njcn.gather.storage.pojo.po.SimAndDigHarmonicResult;
import com.njcn.gather.storage.pojo.po.SimAndDigNonHarmonicResult;
import com.njcn.gather.storage.pojo.param.SingleNonHarmParam;
import com.njcn.gather.storage.service.SimAndDigHarmonicService;
import com.njcn.gather.storage.service.SimAndDigNonHarmonicService;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class ResultServiceImplTest {

    @Test
    public void fillThreePhaseData_harmonicCurrentCarriesBaseCurrentForRateCalculation() throws Exception {
        SimAndDigHarmonicResult result = new SimAndDigHarmonicResult();
        result.setAValue1(detectionData("4.0000", "10.0000"));
        result.setBValue1(detectionData("5.0000", "11.0000"));
        result.setCValue1(detectionData("6.0000", "12.0000"));
        result.setAValue2(detectionData("1.0000"));
        result.setBValue2(detectionData("1.5000"));
        result.setCValue2(detectionData("2.0000"));
        Map<String, String> row = new HashMap<>();

        Method method = ResultServiceImpl.class.getDeclaredMethod(
                "fillThreePhaseData", Object.class, Integer.class, Map.class, String.class);
        method.setAccessible(true);

        boolean filled = (Boolean) method.invoke(newResultService(), result, 2, row, "HI");

        assertTrue(filled);
        assertEquals("4.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_A.getKey()));
        assertEquals("5.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_B.getKey()));
        assertEquals("6.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_C.getKey()));
    }

    @Test
    public void fillThreePhaseData_harmonicCurrentUsesBaseCurrentDataWhenResultDataMissing() throws Exception {
        SimAndDigHarmonicResult result = new SimAndDigHarmonicResult();
        result.setAValue1(detectionDataOnlyData("1.0000"));
        result.setBValue1(detectionDataOnlyData("2.0000"));
        result.setCValue1(detectionDataOnlyData("3.0000"));
        result.setAValue2(detectionData("0.1000"));
        result.setBValue2(detectionData("0.2000"));
        result.setCValue2(detectionData("0.3000"));
        Map<String, String> row = new HashMap<>();

        Method method = ResultServiceImpl.class.getDeclaredMethod(
                "fillThreePhaseData", Object.class, Integer.class, Map.class, String.class);
        method.setAccessible(true);

        boolean filled = (Boolean) method.invoke(newResultService(), result, 2, row, "HI");

        assertTrue(filled);
        assertEquals("1.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_A.getKey()));
        assertEquals("2.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_B.getKey()));
        assertEquals("3.0000", row.get(ItemReportKeyEnum.BASE_CURRENT_C.getKey()));
    }

    @Test
    public void fillHarmonicScriptStandard_harmonicCurrentCopiesScriptPercentToStandardRate() throws Exception {
        Map<String, String> row = new HashMap<>();
        List<PqScriptCheckData> checkData = Arrays.asList(
                checkData("A", 2D, 1D),
                checkData("B", 2D, 1D),
                checkData("C", 2D, 1D)
        );

        Method method = ResultServiceImpl.class.getDeclaredMethod(
                "fillHarmonicScriptStandard", Map.class, String.class, List.class);
        method.setAccessible(true);

        method.invoke(newResultService(), row, "HI", checkData);

        assertEquals("1.0000", row.get(ItemReportKeyEnum.STANDARD_RATE.getKey()));
    }

    @Test
    public void getFinalContent_harmonicPowerCarriesBaseVoltageAndCurrentFromScriptDetails() throws Exception {
        IPqScriptCheckDataService checkDataService = mock(IPqScriptCheckDataService.class);
        SimAndDigHarmonicService harmonicService = mock(SimAndDigHarmonicService.class);
        when(checkDataService.listCheckData(eq("script"), anyList()))
                .thenReturn(Collections.singletonList(checkData("A", 3D, 23.08D)));
        SimAndDigHarmonicResult harmonicResult = new SimAndDigHarmonicResult();
        harmonicResult.setAValue3(detectionData("23.0745", "23.0800"));
        harmonicResult.setBValue3(detectionData("23.0809", "23.0800"));
        harmonicResult.setCValue3(detectionData("23.0814", "23.0800"));
        when(harmonicService.getSingleResult(any())).thenReturn(harmonicResult);
        List<PqScriptDtlDataVO> scriptDetails = Arrays.asList(
                scriptDetail("VOL", "A", 57.7D),
                scriptDetail("CUR", "A", 5.0D)
        );

        SingleTestResult result = newResultService(harmonicService, checkDataService)
                .getFinalContent(scriptDetails, "plan", "dev", 1,
                        Arrays.asList("time", "standardV", "standardI"));

        Map<String, String> row = firstRow(result);
        assertEquals("57.7000", row.get(ItemReportKeyEnum.STANDARD_V.getKey()));
        assertEquals("5.0000", row.get(ItemReportKeyEnum.STANDARD_I.getKey()));
    }

    @Test
    public void getFinalContent_angleUsesCurrentAngleResultRows() throws Exception {
        IPqScriptCheckDataService checkDataService = mock(IPqScriptCheckDataService.class);
        SimAndDigNonHarmonicService nonHarmonicService = mock(SimAndDigNonHarmonicService.class);
        when(checkDataService.listCheckData(eq("script"), anyList())).thenReturn(Collections.emptyList());
        SimAndDigNonHarmonicResult result = new SimAndDigNonHarmonicResult();
        result.setSort(21);
        result.setAdType("I1A");
        result.setAValue(detectionAngleData("61.4", "60", "1.4", 2));
        result.setBValue(detectionAngleData("61", "60", "1", 1));
        result.setCValue(detectionAngleData("61", "60", "1", 1));
        when(nonHarmonicService.queryBySortListWithAdTypeCode(any(SingleNonHarmParam.class)))
                .thenReturn(Collections.singletonList(result));

        SingleTestResult reportResult = newResultService(null, checkDataService, nonHarmonicService)
                .getFinalContent(Collections.singletonList(scriptDetail("Angle", "Base", 21)),
                        "plan", "dev", 1, Arrays.asList("standard", "testA", "testB", "testC"));

        Map<String, String> row = firstRow(reportResult);
        assertEquals("60.0000", row.get(ItemReportKeyEnum.STANDARD.getKey()));
        assertEquals("61.4000", row.get(ItemReportKeyEnum.TEST_A.getKey()));
        assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_B.getKey()));
        assertEquals("61.0000", row.get(ItemReportKeyEnum.TEST_C.getKey()));
        assertEquals("不合格", row.get(ItemReportKeyEnum.RESULT_A.getKey()));
        assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_B.getKey()));
        assertEquals("合格", row.get(ItemReportKeyEnum.RESULT_C.getKey()));
    }

    private ResultServiceImpl newResultService() throws Exception {
        return newResultService(null, null);
    }

    private ResultServiceImpl newResultService(SimAndDigHarmonicService harmonicService,
                                               IPqScriptCheckDataService checkDataService) throws Exception {
        return newResultService(harmonicService, checkDataService, null);
    }

    private ResultServiceImpl newResultService(SimAndDigHarmonicService harmonicService,
                                               IPqScriptCheckDataService checkDataService,
                                               SimAndDigNonHarmonicService nonHarmonicService) throws Exception {
        Constructor<?> constructor = ResultServiceImpl.class.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        Class<?>[] parameterTypes = constructor.getParameterTypes();
        Object[] args = new Object[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            if (nonHarmonicService != null && parameterTypes[i].equals(SimAndDigNonHarmonicService.class)) {
                args[i] = nonHarmonicService;
            } else if (harmonicService != null && parameterTypes[i].equals(SimAndDigHarmonicService.class)) {
                args[i] = harmonicService;
            } else if (checkDataService != null && parameterTypes[i].equals(IPqScriptCheckDataService.class)) {
                args[i] = checkDataService;
            }
        }
        return (ResultServiceImpl) constructor.newInstance(args);
    }

    private Map<String, String> firstRow(SingleTestResult result) {
        return result.getDetail().values().iterator().next()
                .get(0).values().iterator().next()
                .get(0);
    }

    private String detectionData(String value) {
        return detectionData(value, value);
    }

    private String detectionData(String data, String resultData) {
        return "{\"isData\":1,\"data\":" + data + ",\"resultData\":" + resultData
                + ",\"radius\":\"0.05\",\"unit\":\"A\"}";
    }

    private String detectionDataOnlyData(String data) {
        return "{\"num\":1,\"isData\":4,\"data\":" + data + ",\"radius\":\"0.05\",\"unit\":\"A\"}";
    }

    private String detectionAngleData(String data, String resultData, String errorData, int isData) {
        return "{\"isData\":" + isData + ",\"data\":" + data + ",\"resultData\":" + resultData
                + ",\"errorData\":" + errorData + ",\"radius\":\"-1.0~1.0\",\"unit\":\"\\u00b0\"}";
    }

    private PqScriptCheckData checkData(String phase, Double harmNum, Double value) {
        PqScriptCheckData data = new PqScriptCheckData();
        data.setScriptIndex(3);
        data.setValueType("HP");
        data.setPhase(phase);
        data.setHarmNum(harmNum);
        data.setValue(value);
        return data;
    }

    private PqScriptDtlDataVO scriptDetail(String valueType, String phase, Double value) {
        PqScriptDtlDataVO detail = new PqScriptDtlDataVO();
        detail.setScriptId("script");
        detail.setScriptCode("HP");
        detail.setScriptSubType("Base");
        detail.setScriptIndex(3);
        detail.setValueType(valueType);
        detail.setPhase(phase);
        detail.setValue(value);
        return detail;
    }

    private PqScriptDtlDataVO scriptDetail(String scriptCode, String scriptSubType, Integer scriptIndex) {
        PqScriptDtlDataVO detail = new PqScriptDtlDataVO();
        detail.setScriptId("script");
        detail.setScriptCode(scriptCode);
        detail.setScriptSubType(scriptSubType);
        detail.setScriptIndex(scriptIndex);
        return detail;
    }
}
