package jnpf.attendance.schedule;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 第四步：由「相似历史日」在同一 (时间段, 岗位) 上的人数样本，计算稳健目标人数。
 */
public final class SlotPostTargetHeadcountCalculator {

    private SlotPostTargetHeadcountCalculator() {}

    /**
     * 规则：若 {@code max - min <= config.maxMinusMinUseMedianThreshold} 取<strong>中位数</strong>，否则取<strong>P75</strong>（最近序位：
     * 第 {@code ceil(0.75 * n)} 小的数，{@code n} 为样本量）。
     */
    public static int robustTargetFromSamples(List<Integer> samples, SlotPostRobustTargetConfig config) {
        if (config == null) {
            throw new IllegalArgumentException("config");
        }
        if (samples == null || samples.isEmpty()) {
            return 0;
        }
        List<Integer> sorted = new ArrayList<>(samples);
        Collections.sort(sorted);
        int min = sorted.get(0);
        int max = sorted.get(sorted.size() - 1);
        if (max - min <= config.getMaxMinusMinUseMedianThreshold()) {
            return median(sorted);
        }
        return p75NearestRank(sorted);
    }

    /**
     * 供测试观测：明文解释「极差 vs 阈值 → 中位数/P75」的判定与结果（不写业务语义外的推断）。
     */
    public static String describeRobustAggregation(List<Integer> samples, SlotPostRobustTargetConfig config) {
        if (config == null) {
            return "config=null";
        }
        if (samples == null || samples.isEmpty()) {
            return "samples=[] sorted=[] spread=n/a threshold="
                    + config.getMaxMinusMinUseMedianThreshold()
                    + " imputeZero="
                    + config.isImputeZeroForMissingSimilarDays()
                    + " target=0 rule=EMPTY_SAMPLES";
        }
        List<Integer> sorted = new ArrayList<>(samples);
        Collections.sort(sorted);
        int min = sorted.get(0);
        int max = sorted.get(sorted.size() - 1);
        int spread = max - min;
        int thresh = config.getMaxMinusMinUseMedianThreshold();
        boolean useMedian = spread <= thresh;
        int target = robustTargetFromSamples(samples, config);
        String rule = useMedian ? "MEDIAN_STABLE_SPREAD_LE_THRESHOLD" : "P75_VOLATILE_SPREAD_GT_THRESHOLD";
        return "samples="
                + samples
                + " sorted="
                + sorted
                + " sampleSize="
                + sorted.size()
                + " min="
                + min
                + " max="
                + max
                + " spread=max-min="
                + spread
                + " maxMinusMinUseMedianThreshold="
                + thresh
                + " imputeZeroForMissingSimilarDays="
                + config.isImputeZeroForMissingSimilarDays()
                + " target="
                + target
                + " rule="
                + rule;
    }

    /** 升序列表上的中位数，整数结果（偶数个时上取整平均）。 */
    public static int median(List<Integer> sortedAscending) {
        int n = sortedAscending.size();
        if (n == 0) {
            return 0;
        }
        if ((n & 1) == 1) {
            return sortedAscending.get(n / 2);
        }
        int a = sortedAscending.get(n / 2 - 1);
        int b = sortedAscending.get(n / 2);
        return (a + b + 1) / 2;
    }

    /**
     * 最近序位 P75：第 {@code ceil(0.75 * n)} 个最小值（1-based 序位）。
     * 例如 n=5 时为第 4 小 → 对 1,2,2,3,4 得 3。
     */
    public static int p75NearestRank(List<Integer> sortedAscending) {
        int n = sortedAscending.size();
        if (n == 0) {
            return 0;
        }
        int rank1Based = (int) Math.ceil(0.75 * n);
        int idx = Math.min(n - 1, Math.max(0, rank1Based - 1));
        return sortedAscending.get(idx);
    }
}
