package jnpf.attendance.schedule;

import java.io.Serializable;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 排班「五-3」逻辑：同一员工在同一自然日内的<strong>固定班 vs 划线班</strong>互斥账本。
 * <p>
 * 在 {@link ShiftPlanFinalStaffAssigner#assign} / {@link ScheduleStaffAssignmentSteps910#pickForPostDemand}
 * 选人成功时调用 {@link #markFixed} 或 {@link #markLineShift}；选人前调用 {@link #canAssignFixed} /
 * {@link #canAssignLine} 过滤。
 * <p>
 * <b>规则摘要</b>
 * <ul>
 *   <li>同日已划一线 → 不可再排固定班；</li>
 *   <li>同日已排固定班 → 不可再划一线；</li>
 *   <li>同日已排固定班 A → 仅可继续排<strong>相同</strong> {@code fixedShiftGroupKey} 的固定段（多时段同班）；</li>
 *   <li>不同 fixedShiftGroupKey 的固定班在同日互斥。</li>
 * </ul>
 * 划线班允许多段，仅需 {@code lineUsed=true}，不区分段数。
 */
public final class StaffAssignmentDayLedger implements Serializable {

    private final Map<String, Map<LocalDate, DayMarks>> byEmployeeThenDay = new HashMap<>();

    private DayMarks marks(String employeeId, LocalDate day) {
        return byEmployeeThenDay
                .computeIfAbsent(employeeId == null ? "" : employeeId, k -> new HashMap<>())
                .computeIfAbsent(day, d -> new DayMarks());
    }

    /**
     * 是否仍可分配<strong>固定</strong>班段。
     *
     * @param fixedShiftGroupKey 逻辑班次键，见 {@link ShiftPlanFinalStaffAssigner#fixedShiftGroupKey}
     */
    public boolean canAssignFixed(String employeeId, LocalDate day, String fixedShiftGroupKey) {
        DayMarks m = marks(employeeId, day);
        if (m.lineUsed) {
            return false;
        }
        if (m.fixedUsed) {
            return Objects.equals(m.fixedShiftGroupKey, fixedShiftGroupKey);
        }
        return true;
    }

    /** 是否仍可分配<strong>划线</strong>班段（当日尚未占用固定班）。 */
    public boolean canAssignLine(String employeeId, LocalDate day) {
        DayMarks m = marks(employeeId, day);
        return !m.fixedUsed;
    }

    /** 记录该员工当日已排固定班，并绑定逻辑班次键。 */
    public void markFixed(String employeeId, LocalDate day, String fixedShiftGroupKey) {
        DayMarks m = marks(employeeId, day);
        m.fixedUsed = true;
        m.fixedShiftGroupKey = fixedShiftGroupKey;
    }

    /** 记录该员工当日已参与划线班（可多段多次调用）。 */
    public void markLineShift(String employeeId, LocalDate day) {
        DayMarks m = marks(employeeId, day);
        m.lineUsed = true;
    }

    /** 员工 × 自然日 的固定/划线占用标记。 */
    private static final class DayMarks implements Serializable {
        /** 当日是否已排固定班 */
        boolean fixedUsed;
        /** 当日固定班逻辑键（仅 fixedUsed 时有意义） */
        String fixedShiftGroupKey;
        /** 当日是否已排划线班 */
        boolean lineUsed;
    }
}
