package jnpf.attendance.schedule;

import jnpf.model.attendance.vo.attendance.UserWorkSituationVo;
import jnpf.model.attendance.vo.scheduling.FixedSchedulingRuleVo;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Map;

/**
 * {@link StaffRuleEvaluationPort} 的生产实现：结合 {@link FixedSchedulingRuleVo} 与
 * {@link UserWorkSituationVo}（通常来自 {@link SchedulePeriodWorkTracker#asSituationMap()}）判定第十步各维度。
 * <p>
 * 由 {@link AutoScheduleService#buildStaffRuleEvaluationPort} 组装并注入
 * {@link ShiftPlanFinalStaffAssigner#assign}。
 * <p>
 * <b>第九步</b>：当前仅校验 employeeId 非空；请假/借调/资格等待接入其它数据源后扩展
 * {@link #passesOperationalHardConstraints}。
 */
public final class StaffRuleEvaluationPortUsingScheduleRulesAndWorkSituation implements StaffRuleEvaluationPort {

    private final FixedSchedulingRuleVo fixedSchedulingRuleVo;
    private final Map<String, UserWorkSituationVo> situationByUserId;

    /**
     * @param fixedSchedulingRuleVo 考勤组固定排班规则；可为 null（全部 violates* 返回 false）
     * @param situationByUserId     员工工作状态；排班过程中应使用 Tracker 的可变 map
     */
    public StaffRuleEvaluationPortUsingScheduleRulesAndWorkSituation(
            FixedSchedulingRuleVo fixedSchedulingRuleVo,
            Map<String, UserWorkSituationVo> situationByUserId) {
        this.fixedSchedulingRuleVo = fixedSchedulingRuleVo;
        this.situationByUserId =
                situationByUserId == null ? Collections.emptyMap() : situationByUserId;
    }

    @Override
    public boolean passesOperationalHardConstraints(String employeeId, StaffAssignmentContext context) {
        return employeeId != null && !employeeId.trim().isEmpty();
    }

    /**
     * 规则未启用或 cap 无效 → 不违反。
     * 违反条件：当日已排 + 本段 proposed &gt; {@code dailyWorkHoursValue}。
     */
    @Override
    public boolean violatesDailyWorkHoursLimit(String employeeId, StaffAssignmentContext context) {
        FixedSchedulingRuleVo vo = fixedSchedulingRuleVo;
        if (vo == null || !Boolean.TRUE.equals(vo.getDailyWorkHoursEnabled())) {
            return false;
        }
        Integer cap = vo.getDailyWorkHoursValue();
        if (cap == null || cap <= 0) {
            return false;
        }
        double total =
                context.getExistingSameDayScheduledWorkHours() + context.getProposedAssignmentWorkHours();
        return total - cap > 1e-6;
    }

    /**
     * 违反条件：{@code continuousDays >= consecutiveWorkDaysValue}（排班<strong>前</strong>已连续天数已达上限）。
     */
    @Override
    public boolean violatesConsecutiveWorkDaysLimit(String employeeId, StaffAssignmentContext context) {
        FixedSchedulingRuleVo vo = fixedSchedulingRuleVo;
        if (vo == null || !Boolean.TRUE.equals(vo.getConsecutiveWorkDaysEnabled())) {
            return false;
        }
        Integer cap = vo.getConsecutiveWorkDaysValue();
        if (cap == null || cap <= 0) {
            return false;
        }
        int cur = nz(situationOf(employeeId).getContinuousDays());
        return cur >= cap;
    }

    /**
     * 违反条件：{@code weekWorkDays >= weeklyWorkDaysValue}。
     */
    @Override
    public boolean violatesWeeklyWorkDaysLimit(String employeeId, StaffAssignmentContext context) {
        FixedSchedulingRuleVo vo = fixedSchedulingRuleVo;
        if (vo == null || !Boolean.TRUE.equals(vo.getWeeklyWorkDaysEnabled())) {
            return false;
        }
        Integer cap = vo.getWeeklyWorkDaysValue();
        if (cap == null || cap <= 0) {
            return false;
        }
        int cur = nz(situationOf(employeeId).getWeekWorkDays());
        return cur >= cap;
    }

    /**
     * 违反条件：{@code weekWorkHours + proposed > weeklyWorkHoursValue}。
     */
    @Override
    public boolean violatesWeeklyWorkHoursLimit(String employeeId, StaffAssignmentContext context) {
        FixedSchedulingRuleVo vo = fixedSchedulingRuleVo;
        if (vo == null || !Boolean.TRUE.equals(vo.getWeeklyWorkHoursEnabled())) {
            return false;
        }
        Integer cap = vo.getWeeklyWorkHoursValue();
        if (cap == null || cap <= 0) {
            return false;
        }
        BigDecimal wh = situationOf(employeeId).getWeekWorkHours();
        if (wh == null) {
            wh = BigDecimal.ZERO;
        }
        BigDecimal proposed = BigDecimal.valueOf(context.getProposedAssignmentWorkHours());
        return wh.add(proposed).subtract(BigDecimal.valueOf(cap)).doubleValue() > 1e-6;
    }

    /**
     * 休息间隔未知（null）→ 不违反；已知且 &lt; 规则小时数×60 → 违反。
     */
    @Override
    public boolean violatesMinRestBetweenShifts(String employeeId, StaffAssignmentContext context) {
        FixedSchedulingRuleVo vo = fixedSchedulingRuleVo;
        if (vo == null || !Boolean.TRUE.equals(vo.getMinRestBetweenShiftsEnabled())) {
            return false;
        }
        Integer hours = vo.getMinRestBetweenShiftsValue();
        if (hours == null || hours <= 0) {
            return false;
        }
        Integer restMin = context.getMinutesRestSincePriorShiftEnd();
        if (restMin == null) {
            return false;
        }
        return restMin < hours * 60L;
    }

    private UserWorkSituationVo situationOf(String employeeId) {
        if (employeeId == null) {
            return UserWorkSituationVo.builder().build();
        }
        UserWorkSituationVo v = situationByUserId.get(employeeId.trim());
        return v != null ? v : UserWorkSituationVo.builder().build();
    }

    private static int nz(Integer x) {
        return x == null ? 0 : x;
    }
}
