package ru.soune.nocopy.util;

import lombok.experimental.UtilityClass;

import java.util.List;
import java.util.stream.Collectors;

@UtilityClass
public class PercentileCalculator {

    /**
     * Рассчитать несколько перцентилей за один проход
     *
     * @param values      список значений
     * @param percentiles массив перцентилей
     * @return PercentilesResult с соответствующими значениями
     */
    public PercentilesResult calculatePercentiles(List<Long> values, int... percentiles) {
        if (values == null || values.isEmpty()) {
            return new PercentilesResult(0L, 0L, 0L);
        }

        List<Long> sorted = values.stream()
                .sorted()
                .collect(Collectors.toList());

        Long p5 = percentiles.length > 0 ? calculatePercentileFromSorted(sorted, percentiles[0]) : 0L;
        Long p50 = percentiles.length > 1 ? calculatePercentileFromSorted(sorted, percentiles[1]) : 0L;
        Long p95 = percentiles.length > 2 ? calculatePercentileFromSorted(sorted, percentiles[2]) : 0L;

        return new PercentilesResult(p5, p50, p95);
    }

    public PercentilesResult calculateP5P50P95(List<Long> values) {
        return calculatePercentiles(values, 5, 50, 95);
    }

    private Long calculatePercentileFromSorted(List<Long> sorted, double percentile) {
        if (sorted.isEmpty()) {
            return 0L;
        }

        if (percentile <= 0) {
            return sorted.get(0);
        }
        if (percentile >= 100) {
            return sorted.get(sorted.size() - 1);
        }

        double index = (percentile / 100.0) * (sorted.size() - 1);
        int lowerIndex = (int) Math.floor(index);
        int upperIndex = (int) Math.ceil(index);

        if (lowerIndex == upperIndex) {
            return sorted.get(lowerIndex);
        }

        double weight = index - lowerIndex;
        long lowerValue = sorted.get(lowerIndex);
        long upperValue = sorted.get(upperIndex);

        return lowerValue + Math.round(weight * (upperValue - lowerValue));
    }

    public static class PercentilesResult {
        private final Long p5;
        private final Long p50;
        private final Long p95;

        public PercentilesResult(Long p5, Long p50, Long p95) {
            this.p5 = p5;
            this.p50 = p50;
            this.p95 = p95;
        }

        public Long getP5() { return p5; }
        public Long getP50() { return p50; }
        public Long getP95() { return p95; }
    }
}