package jnpf.attendance.schedule;

import jnpf.model.attendance.vo.DayShiftRevenueStatVo;
import jnpf.model.attendance.vo.ShiftPeriodVo;

import java.time.LocalDate;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;

/**
 * 第六、七、八步编排：常见固定班次发现 → 贪心覆盖半小时需求 → 连续缺口划线兜底。
 */
public final class ScheduleDemandCoverSteps678 {

    /** 第七步贪心单次循环上限（防止异常数据死循环）。 */
    public static final int DEFAULT_GREEDY_MAX_ITERATIONS = 5000;

    private ScheduleDemandCoverSteps678() {}

    public static Map<String, DayShiftRevenueStatVo> indexHistoryByDay(List<DayShiftRevenueStatVo> list) {
        Map<String, DayShiftRevenueStatVo> map = new LinkedHashMap<>();
        if (list == null) {
            return map;
        }
        for (DayShiftRevenueStatVo vo : list) {
            if (vo != null && vo.getDay() != null && !vo.getDay().trim().isEmpty()) {
                String trimmed = vo.getDay().trim();
                String key = AttendanceScheduleDayParse.normalizeToIsoDayString(trimmed).orElse(trimmed);
                map.putIfAbsent(key, vo);
            }
        }
        return map;
    }

    public static ScheduleDemandCoverResult coverOneScheduleDay(
            HalfHourPostDemandMatrix matrix,
            List<LocalDate> similarDatesOrdered,
            Map<String, DayShiftRevenueStatVo> historyByDay,
            CommonFixedShiftDiscoveryConfig discoveryCfg,
            LineShiftCoverConfig lineCfg) {
        return coverOneScheduleDay(
                matrix,
                similarDatesOrdered,
                historyByDay,
                discoveryCfg,
                lineCfg,
                null,
                null,
                null,
                DEFAULT_GREEDY_MAX_ITERATIONS);
    }

    public static ScheduleDemandCoverResult coverOneScheduleDay(
            HalfHourPostDemandMatrix matrix,
            List<LocalDate> similarDatesOrdered,
            Map<String, DayShiftRevenueStatVo> historyByDay,
            CommonFixedShiftDiscoveryConfig discoveryCfg,
            LineShiftCoverConfig lineCfg,
            int greedyMaxIterations) {
        return coverOneScheduleDay(
                matrix,
                similarDatesOrdered,
                historyByDay,
                discoveryCfg,
                lineCfg,
                null,
                null,
                null,
                greedyMaxIterations);
    }

    /**
     * @param attendanceShiftPeriods 考勤组班次列表（与 {@code AttendanceShiftSettingService#periodList} 一致）；
     *     {@code null} 时不做考勤班次过滤；非空则仅保留能与考勤组班次匹配上的历史固定班候选。
     */
    public static ScheduleDemandCoverResult coverOneScheduleDay(
            HalfHourPostDemandMatrix matrix,
            List<LocalDate> similarDatesOrdered,
            Map<String, DayShiftRevenueStatVo> historyByDay,
            CommonFixedShiftDiscoveryConfig discoveryCfg,
            LineShiftCoverConfig lineCfg,
            List<ShiftPeriodVo> attendanceShiftPeriods,
            AttendanceGroupShiftMatchConfig shiftMatchCfg,
            Random shiftMatchRandom,
            int greedyMaxIterations) {
        Objects.requireNonNull(matrix, "matrix");
        Objects.requireNonNull(discoveryCfg, "discoveryCfg");
        Objects.requireNonNull(lineCfg, "lineCfg");

        Map<String, List<PostFixedShiftCandidate>> raw =
                CommonFixedShiftDiscovery.discoverCandidatesByPost(
                        similarDatesOrdered, historyByDay, discoveryCfg);
        Map<String, List<PostFixedShiftCandidate>> candidates =
                AttendanceGroupShiftPeriodMatcher.filterCandidatesByAttendancePeriods(
                        raw, attendanceShiftPeriods, shiftMatchCfg, shiftMatchRandom);

        Map<String, FixedShiftCoverOutcome> fixedOut = new LinkedHashMap<>();
        Map<String, List<LineShiftPick>> lineOut = new LinkedHashMap<>();
        Map<String, int[]> remainingFinal = new LinkedHashMap<>();

        for (String post : matrix.getPostIdsOrdered()) {
            List<PostFixedShiftCandidate> cList = candidates.getOrDefault(post, Collections.emptyList());
            FixedShiftCoverOutcome fo =
                    FixedShiftGreedyCoverPlanner.planForPost(matrix, post, cList, greedyMaxIterations);
            fixedOut.put(post, fo);

            int n = fo.getSlotLabelsOrdered().size();
            int[] rem = new int[n];
            for (int i = 0; i < n; i++) {
                rem[i] = fo.remainingNeedAt(i);
            }
            List<LineShiftPick> linePicks = LineShiftGapPlanner.planForPost(matrix, post, rem, lineCfg);
            lineOut.put(post, linePicks);
            for (LineShiftPick p : linePicks) {
                for (int k = p.getStartSlotIndexInclusive(); k < p.getEndSlotIndexExclusive(); k++) {
                    rem[k] = Math.max(0, rem[k] - 1);
                }
            }
            remainingFinal.put(post, rem);
        }

        return new ScheduleDemandCoverResult(
                matrix.getScheduleDay(), candidates, fixedOut, lineOut, remainingFinal);
    }

    /**
     * 对区间内每个已有需求矩阵的排班日执行覆盖（仅处理 {@code matrices} 中存在的 key）。
     */
    public static Map<LocalDate, ScheduleDemandCoverResult> coverAllScheduleDays(
            Map<LocalDate, HalfHourPostDemandMatrix> matrices,
            Map<LocalDate, ScheduleTemplateSimilarDaysResult> similarByScheduleDay,
            Map<String, DayShiftRevenueStatVo> historyByDay,
            CommonFixedShiftDiscoveryConfig discoveryCfg,
            LineShiftCoverConfig lineCfg) {
        return coverAllScheduleDays(
                matrices,
                similarByScheduleDay,
                historyByDay,
                discoveryCfg,
                lineCfg,
                null,
                null,
                null);
    }

    public static Map<LocalDate, ScheduleDemandCoverResult> coverAllScheduleDays(
            Map<LocalDate, HalfHourPostDemandMatrix> matrices,
            Map<LocalDate, ScheduleTemplateSimilarDaysResult> similarByScheduleDay,
            Map<String, DayShiftRevenueStatVo> historyByDay,
            CommonFixedShiftDiscoveryConfig discoveryCfg,
            LineShiftCoverConfig lineCfg,
            List<ShiftPeriodVo> attendanceShiftPeriods,
            AttendanceGroupShiftMatchConfig shiftMatchCfg,
            Random shiftMatchRandom) {
        if (matrices == null || matrices.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<LocalDate, ScheduleDemandCoverResult> out = new LinkedHashMap<>();
        for (Map.Entry<LocalDate, HalfHourPostDemandMatrix> e : matrices.entrySet()) {
            LocalDate day = e.getKey();
            HalfHourPostDemandMatrix m = e.getValue();
            if (day == null || m == null) {
                continue;
            }
            ScheduleTemplateSimilarDaysResult sim =
                    similarByScheduleDay == null ? null : similarByScheduleDay.get(day);
            if (sim == null || !sim.isSuccess()) {
                continue;
            }
            out.put(
                    day,
                    coverOneScheduleDay(
                            m,
                            sim.getSimilarDates(),
                            historyByDay,
                            discoveryCfg,
                            lineCfg,
                            attendanceShiftPeriods,
                            shiftMatchCfg,
                            shiftMatchRandom,
                            DEFAULT_GREEDY_MAX_ITERATIONS));
        }
        return Collections.unmodifiableMap(out);
    }
}
