package com.njcn.gather.steady.datavie.service.impl;

import com.njcn.gather.steady.datavie.component.SteadyInfluxQueryComponent;
import com.njcn.gather.steady.datavie.component.SteadyTrendFieldResolver;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
import com.njcn.gather.steady.datavie.pojo.param.SteadyTrendQueryParam;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendQueryVO;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSeriesVO;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryItemVO;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendSummaryVO;
import com.njcn.gather.steady.datavie.service.SteadyDataViewTrendService;
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
import com.njcn.gather.tool.addledger.service.AddLedgerService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * 稳态趋势查询服务实现。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SteadyDataViewTrendServiceImpl implements SteadyDataViewTrendService {

    private static final String EMPTY_TEXT = "-";
    private static final String LINE_VOLTAGE_RMS_INDICATOR = "V_LINE_RMS";

    private final SteadyTrendFieldResolver fieldResolver;
    private final SteadyInfluxQueryComponent influxQueryComponent;
    private final AddLedgerService addLedgerService;

    @Override
    public SteadyTrendQueryVO queryTrend(SteadyTrendQueryParam param) {
        return queryTrendInternal(param);
    }

    @Override
    public SteadyTrendQueryVO queryTrendDay(SteadyTrendQueryParam param) {
        return queryTrendInternal(param);
    }

    @Override
    public SteadyTrendSummaryVO summarizeTrend(SteadyTrendQueryParam param) {
        SteadyTrendQueryParam summaryParam = copyParam(param);
        SteadyTrendQueryVO trend = queryTrendInternal(summaryParam);
        SteadyTrendSummaryVO result = new SteadyTrendSummaryVO();
        for (SteadyTrendSeriesVO series : trend.getSeries()) {
            result.getItems().add(buildSummaryItem(series));
        }
        return result;
    }

    private SteadyTrendQueryVO queryTrendInternal(SteadyTrendQueryParam param) {
        LocalDateTime startTime = fieldResolver.parseRequiredTime(param == null ? null : param.getTimeStart(), "开始时间不能为空");
        LocalDateTime endTime = fieldResolver.parseRequiredTime(param == null ? null : param.getTimeEnd(), "结束时间不能为空");
        List<SteadyTrendResolvedFieldBO> fields = fieldResolver.resolveFields(param);
        enrichLineNames(fields);

        SteadyTrendQueryVO result = new SteadyTrendQueryVO();
        result.setSampled(false);
        result.setLoadableDays(resolveLoadableDays(startTime, endTime));
        int displayPointCount = 0;
        long startMillis = System.currentTimeMillis();
        log.info("稳态趋势查询开始，seriesCount={}，timeStart={}，timeEnd={}，qualityFlag={}", fields.size(), startTime, endTime, param.getQualityFlag());
        for (SteadyTrendResolvedFieldBO field : fields) {
            List<SteadyTrendPointVO> points = influxQueryComponent.queryTrendPoints(field, startTime, endTime, param.getQualityFlag());
            displayPointCount += points.size();
            result.getSeries().add(buildSeries(field, points));
        }
        log.info("稳态趋势查询结束，seriesCount={}，displayPointCount={}，costMs={}", fields.size(), displayPointCount, System.currentTimeMillis() - startMillis);
        /*
         * 当前 Influx 查询按曲线独立执行，未额外发 count 查询；sourcePointCount 保持与实际返回点数一致。
         * 后续如需要精确原始点数，可单独增加 count(field) 查询。
         */
        result.setDisplayPointCount(displayPointCount);
        result.setSourcePointCount(displayPointCount);
        return result;
    }

    private SteadyTrendSeriesVO buildSeries(SteadyTrendResolvedFieldBO field, List<SteadyTrendPointVO> points) {
        SteadyTrendSeriesVO series = new SteadyTrendSeriesVO();
        series.setSeriesKey(field.getSeriesKey());
        series.setLineId(field.getLineId());
        series.setLineName(field.getLineName());
        series.setIndicatorCode(field.getIndicatorCode());
        series.setIndicatorName(field.getIndicatorName());
        series.setSeriesName(field.getSeriesName());
        series.setPhase(resolveDisplayPhase(field));
        series.setStatType(field.getStatType());
        series.setUnit(field.getUnit());
        series.setPoints(points);
        return series;
    }

    private String resolveDisplayPhase(SteadyTrendResolvedFieldBO field) {
        if (!LINE_VOLTAGE_RMS_INDICATOR.equals(field.getIndicatorCode())) {
            return field.getPhase();
        }
        if ("A".equals(field.getPhase())) {
            return "AB";
        }
        if ("B".equals(field.getPhase())) {
            return "BC";
        }
        if ("C".equals(field.getPhase())) {
            return "CA";
        }
        return field.getPhase();
    }

    private void enrichLineNames(List<SteadyTrendResolvedFieldBO> fields) {
        List<String> lineIds = new ArrayList<String>();
        for (SteadyTrendResolvedFieldBO field : fields) {
            if (!lineIds.contains(field.getLineId())) {
                lineIds.add(field.getLineId());
            }
        }
        Map<String, AddLedgerLinePathVO> linePathMap = addLedgerService.listLinePathByLineIds(lineIds);
        for (SteadyTrendResolvedFieldBO field : fields) {
            AddLedgerLinePathVO linePath = linePathMap.get(field.getLineId());
            field.setLineName(linePath == null || trimToNull(linePath.getLineName()) == null ? EMPTY_TEXT : linePath.getLineName());
        }
    }

    private SteadyTrendSummaryItemVO buildSummaryItem(SteadyTrendSeriesVO series) {
        SteadyTrendSummaryItemVO item = new SteadyTrendSummaryItemVO();
        item.setSeriesKey(series.getSeriesKey());
        List<BigDecimal> values = new ArrayList<BigDecimal>();
        for (SteadyTrendPointVO point : series.getPoints()) {
            if (point.getValue() != null) {
                values.add(point.getValue());
            }
        }
        if (values.isEmpty()) {
            return item;
        }
        values.sort(Comparator.naturalOrder());
        BigDecimal sum = BigDecimal.ZERO;
        for (BigDecimal value : values) {
            sum = sum.add(value);
        }
        item.setMin(values.get(0));
        item.setMax(values.get(values.size() - 1));
        item.setAvg(sum.divide(new BigDecimal(values.size()), 6, RoundingMode.HALF_UP));
        int cp95Index = Math.max(0, (int) Math.ceil(values.size() * 0.95D) - 1);
        item.setCp95(values.get(cp95Index));
        return item;
    }

    private List<String> resolveLoadableDays(LocalDateTime startTime, LocalDateTime endTime) {
        List<String> result = new ArrayList<String>();
        LocalDate date = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();
        while (!date.isAfter(endDate)) {
            result.add(date.toString());
            date = date.plusDays(1);
        }
        return result;
    }

    private SteadyTrendQueryParam copyParam(SteadyTrendQueryParam source) {
        SteadyTrendQueryParam target = new SteadyTrendQueryParam();
        if (source == null) {
            return target;
        }
        target.setLineIds(copyList(source.getLineIds()));
        target.setIndicatorCodes(copyList(source.getIndicatorCodes()));
        target.setStatTypes(copyList(source.getStatTypes()));
        target.setTimeStart(source.getTimeStart());
        target.setTimeEnd(source.getTimeEnd());
        target.setQualityFlag(source.getQualityFlag());
        target.setHarmonicOrders(source.getHarmonicOrders() == null ? Collections.<Integer>emptyList() : new ArrayList<Integer>(source.getHarmonicOrders()));
        return target;
    }

    private List<String> copyList(List<String> source) {
        return source == null ? new ArrayList<String>() : new ArrayList<String>(source);
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

}
