package com.njcn.gather.tool.wave.component;

import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.tool.wave.pojo.dto.WaveCycleVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveDataDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveHarmonicDTO;
import com.njcn.gather.tool.wave.pojo.dto.WavePhaseVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveSequenceUnbalanceDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveSequenceVectorDTO;
import com.njcn.gather.tool.wave.pojo.dto.WaveVectorGroupDTO;
import com.njcn.gather.tool.wave.pojo.enums.WaveFileResponseEnum;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 波形向量计算组件。
 */
@Component
public class WaveVectorComponent {

    /** 最低谐波次数。 */
    private static final int MIN_HARMONIC_ORDER = 1;
    /** 最高谐波次数。 */
    private static final int MAX_HARMONIC_ORDER = 50;

    /**
     * 基于原始波形按周波计算向量结果。
     */
    public List<WaveVectorGroupDTO> calculateVectors(WaveDataDTO waveDataDTO) {
        if (waveDataDTO == null || waveDataDTO.getComtradeCfgDTO() == null) {
            throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
        }
        List<List<Float>> listWaveData = waveDataDTO.getListWaveData();
        Integer samplePerCycle = waveDataDTO.getComtradeCfgDTO().getFinalSampleRate();
        if (listWaveData == null || listWaveData.isEmpty() || samplePerCycle == null || samplePerCycle <= 0 || listWaveData.size() < samplePerCycle) {
            throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
        }

        int phaseCount = waveDataDTO.getIPhasic() == null || waveDataDTO.getIPhasic() <= 0 ? 1 : waveDataDTO.getIPhasic();
        int totalValueColumnCount = listWaveData.get(0).size() - 1;
        if (totalValueColumnCount < phaseCount) {
            throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
        }

        int groupCount = totalValueColumnCount / phaseCount;
        int cycleCount = listWaveData.size() / samplePerCycle;
        List<WaveVectorGroupDTO> vectorGroups = new ArrayList<>();
        for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
            int titleIndex = groupIndex * phaseCount + 1;
            WaveVectorGroupDTO groupDTO = new WaveVectorGroupDTO();
            groupDTO.setChannelName(resolveChannelName(waveDataDTO.getChannelNames(), titleIndex, groupIndex));
            groupDTO.setUnit(resolveVectorUnit(waveDataDTO, titleIndex));
            groupDTO.setPhaseCount(phaseCount);
            groupDTO.setPhaseNames(resolvePhaseNames(waveDataDTO, titleIndex, phaseCount));
            groupDTO.setVectorSeries(buildVectorSeries(waveDataDTO, groupIndex, phaseCount, samplePerCycle, cycleCount));
            vectorGroups.add(groupDTO);
        }
        return vectorGroups;
    }

    /**
     * 构建单组通道的周波结果序列。
     */
    private List<WaveCycleVectorDTO> buildVectorSeries(WaveDataDTO waveDataDTO, int groupIndex, int phaseCount,
                                                       int samplePerCycle, int cycleCount) {
        List<WaveCycleVectorDTO> vectorSeries = new ArrayList<>();
        int titleIndex = groupIndex * phaseCount + 1;
        List<String> phaseNames = resolvePhaseNames(waveDataDTO, titleIndex, phaseCount);
        float ratio = resolveVectorRatio(waveDataDTO, titleIndex);
        for (int cycleIndex = 0; cycleIndex < cycleCount; cycleIndex++) {
            int startIndex = cycleIndex * samplePerCycle;
            vectorSeries.add(buildCycleVector(waveDataDTO.getListWaveData(), phaseNames, groupIndex, phaseCount,
                    samplePerCycle, startIndex, cycleIndex, ratio, resolveVectorUnit(waveDataDTO, titleIndex)));
        }
        return vectorSeries;
    }

    /**
     * 计算单个周波的电能质量结果。
     */
    private WaveCycleVectorDTO buildCycleVector(List<List<Float>> listWaveData, List<String> phaseNames, int groupIndex,
                                                int phaseCount, int samplePerCycle, int startIndex, int cycleIndex,
                                                float ratio, String unit) {
        WaveCycleVectorDTO cycleVectorDTO = new WaveCycleVectorDTO();
        cycleVectorDTO.setCycleIndex(cycleIndex);
        cycleVectorDTO.setTime(resolveCycleTime(listWaveData, startIndex, samplePerCycle));

        List<WavePhaseVectorDTO> phaseVectors = new ArrayList<>();
        Map<String, HarmonicSpectrum> spectrumMap = new HashMap<>();
        for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
            int columnIndex = 1 + groupIndex * phaseCount + phaseIndex;
            HarmonicSpectrum spectrum = calculateSpectrum(listWaveData, startIndex, samplePerCycle, columnIndex, ratio);
            String phaseName = phaseNames.get(phaseIndex);
            phaseVectors.add(buildPhaseVector(phaseName, spectrum, unit));
            spectrumMap.put(phaseName, spectrum);
        }
        cycleVectorDTO.setPhaseVectors(phaseVectors);

        if (phaseCount >= 3) {
            HarmonicSpectrum phaseA = findSpectrum(spectrumMap, "A");
            HarmonicSpectrum phaseB = findSpectrum(spectrumMap, "B");
            HarmonicSpectrum phaseC = findSpectrum(spectrumMap, "C");
            ComplexValue alpha = new ComplexValue(-0.5D, Math.sqrt(3D) / 2D);
            ComplexValue alphaSquare = new ComplexValue(-0.5D, -Math.sqrt(3D) / 2D);
            ComplexValue positive = phaseA.fundamental.add(alpha.multiply(phaseB.fundamental)).add(alphaSquare.multiply(phaseC.fundamental)).divide(3D);
            ComplexValue negative = phaseA.fundamental.add(alphaSquare.multiply(phaseB.fundamental)).add(alpha.multiply(phaseC.fundamental)).divide(3D);
            ComplexValue zero = phaseA.fundamental.add(phaseB.fundamental).add(phaseC.fundamental).divide(3D);
            cycleVectorDTO.setPositiveSequence(buildSequenceVector("正序", positive));
            cycleVectorDTO.setNegativeSequence(buildSequenceVector("负序", negative));
            cycleVectorDTO.setZeroSequence(buildSequenceVector("零序", zero));
            cycleVectorDTO.setUnbalance(buildUnbalance(positive, negative, zero));
        }
        return cycleVectorDTO;
    }

    /**
     * 组装单相结果。
     */
    private WavePhaseVectorDTO buildPhaseVector(String phaseName, HarmonicSpectrum spectrum, String unit) {
        WavePhaseVectorDTO dto = new WavePhaseVectorDTO();
        dto.setPhaseName(phaseName);
        dto.setTotalRms(roundValue(spectrum.totalRms));
        dto.setFundamentalAmplitude(roundValue(spectrum.fundamental.magnitude()));
        dto.setFundamentalRms(roundValue(spectrum.fundamental.magnitude() / Math.sqrt(2D)));
        dto.setFundamentalPhaseAngle(roundValue(resolveDisplayAngle(spectrum.fundamental)));
        dto.setHarmonicDistortionRate(roundValue(calculateThdRate(spectrum)));
        if ("kV".equalsIgnoreCase(unit)) {
            dto.setHarmonicVoltageContentRates(buildHarmonicList(spectrum, true));
        } else {
            dto.setHarmonicCurrentAmplitudes(buildHarmonicList(spectrum, false));
        }
        return dto;
    }

    /**
     * 计算单相一个周波的频谱信息。
     */
    private HarmonicSpectrum calculateSpectrum(List<List<Float>> listWaveData, int startIndex, int samplePerCycle,
                                               int columnIndex, float ratio) {
        double totalSquare = 0D;
        ComplexValue[] harmonics = new ComplexValue[MAX_HARMONIC_ORDER + 1];
        for (int order = MIN_HARMONIC_ORDER; order <= MAX_HARMONIC_ORDER; order++) {
            double real = 0D;
            double imag = 0D;
            for (int i = 0; i < samplePerCycle; i++) {
                List<Float> row = listWaveData.get(startIndex + i);
                if (row == null || row.size() <= columnIndex) {
                    throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
                }
                double sample = row.get(columnIndex) * ratio;
                if (order == 1) {
                    totalSquare += sample * sample;
                }
                double angle = 2D * Math.PI * order * i / samplePerCycle;
                real += sample * Math.cos(angle);
                imag -= sample * Math.sin(angle);
            }
            harmonics[order] = new ComplexValue(2D * real / samplePerCycle, 2D * imag / samplePerCycle);
        }
        HarmonicSpectrum spectrum = new HarmonicSpectrum();
        spectrum.harmonics = harmonics;
        spectrum.fundamental = harmonics[1];
        spectrum.totalRms = Math.sqrt(totalSquare / samplePerCycle);
        return spectrum;
    }

    /**
     * 构建谐波列表。
     */
    private List<WaveHarmonicDTO> buildHarmonicList(HarmonicSpectrum spectrum, boolean buildRate) {
        List<WaveHarmonicDTO> harmonics = new ArrayList<>();
        double fundamentalAmplitude = spectrum.fundamental == null ? 0D : spectrum.fundamental.magnitude();
        for (int order = 2; order <= MAX_HARMONIC_ORDER; order++) {
            ComplexValue value = spectrum.harmonics[order];
            if (value == null) {
                continue;
            }
            WaveHarmonicDTO harmonicDTO = new WaveHarmonicDTO();
            harmonicDTO.setHarmonicOrder(order);
            harmonicDTO.setAmplitude(roundValue(value.magnitude()));
            harmonicDTO.setRms(roundValue(value.magnitude() / Math.sqrt(2D)));
            if (buildRate) {
                harmonicDTO.setRate(roundValue(fundamentalAmplitude == 0D ? 0D : value.magnitude() / fundamentalAmplitude * 100D));
            }
            harmonics.add(harmonicDTO);
        }
        return harmonics;
    }

    /**
     * 计算 THD。
     */
    private double calculateThdRate(HarmonicSpectrum spectrum) {
        double sum = 0D;
        for (int order = 2; order <= MAX_HARMONIC_ORDER; order++) {
            ComplexValue value = spectrum.harmonics[order];
            if (value != null) {
                double rms = value.magnitude() / Math.sqrt(2D);
                sum += rms * rms;
            }
        }
        double fundamentalRms = spectrum.fundamental == null ? 0D : spectrum.fundamental.magnitude() / Math.sqrt(2D);
        if (fundamentalRms == 0D) {
            return 0D;
        }
        return Math.sqrt(sum) / fundamentalRms * 100D;
    }

    /**
     * 组装序分量结果。
     */
    private WaveSequenceVectorDTO buildSequenceVector(String sequenceName, ComplexValue value) {
        WaveSequenceVectorDTO dto = new WaveSequenceVectorDTO();
        dto.setSequenceName(sequenceName);
        dto.setAmplitude(roundValue(value.magnitude()));
        dto.setRms(roundValue(value.magnitude() / Math.sqrt(2D)));
        dto.setPhaseAngle(roundValue(resolveDisplayAngle(value)));
        return dto;
    }

    /**
     * 组装不平衡度。
     */
    private WaveSequenceUnbalanceDTO buildUnbalance(ComplexValue positive, ComplexValue negative, ComplexValue zero) {
        WaveSequenceUnbalanceDTO dto = new WaveSequenceUnbalanceDTO();
        double positiveMagnitude = positive.magnitude();
        if (positiveMagnitude == 0D) {
            dto.setNegativeUnbalanceRate(0F);
            dto.setZeroUnbalanceRate(0F);
            return dto;
        }
        dto.setNegativeUnbalanceRate(roundValue(negative.magnitude() / positiveMagnitude * 100D));
        dto.setZeroUnbalanceRate(roundValue(zero.magnitude() / positiveMagnitude * 100D));
        return dto;
    }

    /**
     * 查找频谱。
     */
    private HarmonicSpectrum findSpectrum(Map<String, HarmonicSpectrum> spectrumMap, String phaseFlag) {
        for (Map.Entry<String, HarmonicSpectrum> entry : spectrumMap.entrySet()) {
            if (StrUtil.containsIgnoreCase(entry.getKey(), phaseFlag)) {
                return entry.getValue();
            }
        }
        return HarmonicSpectrum.empty();
    }

    /**
     * 解析通道名称。
     */
    private String resolveChannelName(List<String> channelNames, int titleIndex, int groupIndex) {
        if (channelNames != null && channelNames.size() > titleIndex) {
            return channelNames.get(titleIndex);
        }
        return "CH" + (groupIndex + 1);
    }

    /**
     * 解析相别名称。
     */
    private List<String> resolvePhaseNames(WaveDataDTO waveDataDTO, int titleIndex, int phaseCount) {
        List<String> phaseNames = new ArrayList<>();
        List<String> waveTitle = waveDataDTO.getWaveTitle();
        for (int phaseIndex = 0; phaseIndex < phaseCount; phaseIndex++) {
            if (waveTitle != null && waveTitle.size() > titleIndex + phaseIndex) {
                String title = waveTitle.get(titleIndex + phaseIndex);
                phaseNames.add(title.length() > 1 ? title.substring(1) : title);
            } else {
                phaseNames.add(defaultPhaseName(phaseIndex));
            }
        }
        return phaseNames;
    }

    /**
     * 解析向量单位。
     */
    private String resolveVectorUnit(WaveDataDTO waveDataDTO, int titleIndex) {
        if (waveDataDTO.getWaveTitle() != null && waveDataDTO.getWaveTitle().size() > titleIndex
                && StrUtil.startWithIgnoreCase(waveDataDTO.getWaveTitle().get(titleIndex), "U")) {
            return "kV";
        }
        return "A";
    }

    /**
     * 解析向量比例。
     */
    private float resolveVectorRatio(WaveDataDTO waveDataDTO, int titleIndex) {
        if (waveDataDTO.getWaveTitle() != null && waveDataDTO.getWaveTitle().size() > titleIndex
                && StrUtil.startWithIgnoreCase(waveDataDTO.getWaveTitle().get(titleIndex), "U")) {
            // 电压向量按 kV 展示，需与普通波形查看保持一致，避免将 V 级数值直接标记为 kV。
            return waveDataDTO.getPt() == null ? 0.001F : waveDataDTO.getPt().floatValue() / 1000F;
        }
        return waveDataDTO.getCt() == null ? 1F : waveDataDTO.getCt().floatValue();
    }

    /**
     * 取当前周波中点时刻。
     */
    private Float resolveCycleTime(List<List<Float>> listWaveData, int startIndex, int samplePerCycle) {
        int middleIndex = Math.min(startIndex + samplePerCycle / 2, listWaveData.size() - 1);
        List<Float> row = listWaveData.get(middleIndex);
        if (row == null || row.isEmpty()) {
            throw new BusinessException(WaveFileResponseEnum.WAVE_DATA_INVALID);
        }
        return roundValue(row.get(0));
    }

    /**
     * 傅里叶相角换算成正弦基准显示角度。
     */
    private double resolveDisplayAngle(ComplexValue vectorValue) {
        return normalizeAngle(Math.toDegrees(vectorValue.angle()) + 90D);
    }

    /**
     * 归一化角度。
     */
    private double normalizeAngle(double angle) {
        double normalizedAngle = angle;
        while (normalizedAngle > 180D) {
            normalizedAngle -= 360D;
        }
        while (normalizedAngle <= -180D) {
            normalizedAngle += 360D;
        }
        return normalizedAngle;
    }

    /**
     * 统一保留 4 位小数。
     */
    private Float roundValue(double value) {
        return BigDecimal.valueOf(value).setScale(4, RoundingMode.HALF_UP).floatValue();
    }

    /**
     * 默认相名。
     */
    private String defaultPhaseName(int phaseIndex) {
        switch (phaseIndex) {
            case 0:
                return "A相";
            case 1:
                return "B相";
            case 2:
                return "C相";
            default:
                return "相" + (phaseIndex + 1);
        }
    }

    /**
     * 频谱对象。
     */
    private static final class HarmonicSpectrum {
        /** 各次谐波复向量。 */
        private ComplexValue[] harmonics;
        /** 基波。 */
        private ComplexValue fundamental;
        /** 总有效值。 */
        private double totalRms;

        private static HarmonicSpectrum empty() {
            HarmonicSpectrum spectrum = new HarmonicSpectrum();
            spectrum.harmonics = new ComplexValue[MAX_HARMONIC_ORDER + 1];
            spectrum.fundamental = ComplexValue.zero();
            spectrum.totalRms = 0D;
            return spectrum;
        }
    }

    /**
     * 复向量。
     */
    private static final class ComplexValue {
        /** 实部。 */
        private final double real;
        /** 虚部。 */
        private final double imag;

        private ComplexValue(double real, double imag) {
            this.real = real;
            this.imag = imag;
        }

        private static ComplexValue zero() {
            return new ComplexValue(0D, 0D);
        }

        private ComplexValue add(ComplexValue other) {
            return new ComplexValue(this.real + other.real, this.imag + other.imag);
        }

        private ComplexValue multiply(ComplexValue other) {
            return new ComplexValue(
                    this.real * other.real - this.imag * other.imag,
                    this.real * other.imag + this.imag * other.real
            );
        }

        private ComplexValue divide(double divisor) {
            return new ComplexValue(this.real / divisor, this.imag / divisor);
        }

        private double magnitude() {
            return Math.hypot(this.real, this.imag);
        }

        private double angle() {
            return Math.atan2(this.imag, this.real);
        }
    }
}
