package jnpf.attendance.service;

import jnpf.base.service.SuperService;
import jnpf.entity.AttendanceGroup;
import jnpf.entity.AttendanceGroupUser;
import jnpf.entity.attendance.*;
import jnpf.enums.attendance.AttendanceTypeEnum;
import jnpf.enums.attendance.v2.ClockOutHandleParam;
import jnpf.exception.HandleException;
import jnpf.model.attendance.dto.LineDrawingSchedulesConfigDto;
import jnpf.model.attendance.dto.PeriodConfig;
import jnpf.model.attendance.dto.SchedulesImportDto;
import jnpf.model.attendance.dto.SchedulesSetDto;
import jnpf.model.attendance.dto.UnifiedSchedulesDto;
import jnpf.model.attendance.model.DayClockRange;
import jnpf.model.attendance.vo.*;
import jnpf.model.attendance.vo.attendance.OutOrBusApproveVo;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 考勤组-每日出勤规则 服务类
 * </p>
 *
 * @author ahua
 * @since 2023-11-22
 */
public interface AttendanceDailyRuleService extends SuperService<FtbAttendanceDailyRule> {

    /**
     * 导出指定月份的调度信息
     *
     * @param groupId       调度组ID，用于标识一组调度信息
     * @param realName      用户真实姓名，用于过滤特定用户的调度信息
     * @param month         指定的月份，格式为"YYYY-MM"，用于获取该月的调度信息
     * @param userIdList    用户ID列表，用于过滤需要导出调度信息的用户
     * @param isSchedules   是否包含调度信息的标志，用于指示是否需要导出调度详情
     */
    void schedulesExport(String groupId, String workGroupId, String realName, String month, List<String> userIdList, Integer isSchedules);

    void hisDailyRule(Map<String, String> group2orgMap, Map<String, String> org2groupMap, Map<String, List<AttendanceGroupUser>> usersMap);

    void clockOutHandle(ClockOutHandleParam param);

    FtbAttendanceDailyRule workOvertimeNotApprove(ClockOutHandleParam param);

    /**
     * 根据条件获取排班列表V2版本。
     *
     * @param groupId     考勤组ID
     * @param realName    真实姓名
     * @param month       月份
     * @param userIdList  用户ID列表
     * @param isSchedules 是否排班标识
     * @return 排班列表V2版本
     */
    List<SchedulesV2Vo> getSchedulesListV2(String groupId, String workGroupId, String realName, String month, List<String> userIdList, Integer isSchedules);

    /**
     * 根据条件获取排班列表 V2（按开始日期、结束日期，其它与 {@link #getSchedulesListV2} 一致）
     *
     * @param startDate   开始日期（yyyy-MM-dd）
     * @param endDate     结束日期（yyyy-MM-dd）
     */
    List<SchedulesV2Vo> getSchedulesListV2ByDateRange(String groupId, String workGroupId, String realName, String startDate, String endDate, List<String> userIdList, Integer isSchedules);

    /**
     * 按预排班 Redis 草稿（{@link jnpf.attendance.schedule.ShiftPlanAssignmentResult#getByEmployee()} JSON，key 为租户 + 草稿 id）
     * 转换为排班列表 V2 结构；草稿不存在或为空返回空列表。
     *
     * @param groupId   考勤组 id
     * @param startDate 开始日期 yyyy-MM-dd
     * @param endDate   结束日期 yyyy-MM-dd
     * @param draftId   草稿 id（Redis key 第二段）
     */
    List<SchedulesV2Vo> getSchedulesListV2ByPreScheduleDraft(
            String groupId, String startDate, String endDate, String draftId);

    /**
     * 根据班次ID列表获取班次名称实体映射。
     *
     * @param shiftIds 班次ID列表
     * @return 班次ID与班次名称实体的映射
     */
    Map<String, AttendanceShiftNameEntity> getShiftByShiftIds(List<String> shiftIds);

    /**
     * 检查指定日期用户是否存在。
     *
     * @param users 考勤组用户列表
     * @param date  日期
     * @return 用户存在标识(- 1离组 0未加入 1存在 2借调)
     */
    Integer findUserIsExistsStatusByDay(List<AttendanceGroupUser> users, Date date);

    /**
     * 检查指定日期用户是否存在。
     *
     * @param users 考勤组用户列表
     * @param date  日期
     * @return 用户存在标识（1存在，0不存在）
     */
    Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date date);

    /**
     * 检查指定日期范围内用户是否存在。
     *
     * @param users 考勤组用户列表
     * @param start 开始日期
     * @param end   结束日期
     * @return 用户存在标识（1存在，0不存在）
     */
    Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date start, Date end);

    /**
     * 检查指定日期范围内用户是否存在。
     *
     * @param users 考勤组用户列表
     * @param start 开始日期
     * @param end   结束日期
     * @return 用户存在标识（1存在，0不存在）
     */
    Map<String, Integer> findUserIsExistsByUserList(List<AttendanceGroupUser> users, Date start, Date end);

    /**
     * 判断当天内用户的状态， -1为已离 0为未入 1正常 2为全天被借调 3部分被借调 4借调
     *
     * @param users 考勤组用户列表
     * @param date  日期
     * @return  -1为已离 0为未入 1正常 2为全天被借调 3部分被借调 4借调
     */
    Integer isExistStatus(List<AttendanceGroupUser> users, Date date);

    /**
     * 判断时间范围内用户的状态， -1为已离 0为未入 1正常 2为全天被借调 3部分被借调 4借调
     * @param users
     * @param start
     * @param end
     * @return
     */
    Integer isExistStatus(List<AttendanceGroupUser> users, Date start, Date end);

    /**
     * 是否借调，不管借调还是被借调，只要命中借调时间，则返回true
     *
     * @param users 考勤组用户列表
     * @param start 开始日期
     * @param end   结束日期
     * @return 是否在借调状态
     */
    Boolean isInSecondment(List<AttendanceGroupUser> users, Date start, Date end);

    /**
     * 获取排班规则详情。
     *
     * @param id 排班规则ID
     * @return 排班规则详情
     * @throws HandleException 处理异常
     */
    ScheduleRuleDetailVo getDetail(String id) throws HandleException;

    /**
     * 修改固定周期班次设置。
     *
     * @param groupIds   考勤组ID列表
     * @param periodList 周期班次设置列表
     * @param enableTime 生效时间
     */
    void fixedPeriodChange(List<String> groupIds, AttendanceShiftSettingVo periodList, Date enableTime);

    /**
     * 添加用户的固定班次规则处理。
     *
     * @param groupId 考勤组ID
     * @param userIds 用户ID列表
     */
    void addUserFixedHandle(String groupId, List<String> userIds);

    /**
     * 添加用户的固定班次规则处理，包含租户ID。
     *
     * @param groupId  考勤组ID
     * @param tenantId 租户ID
     * @param userIds  用户ID列表
     */
    void addUserFixedHandle(String groupId, String tenantId, List<String> userIds);

    /**
     * 添加节假日日常规则。
     *
     * @param start              开始日期
     * @param end                结束
     * @param attendanceGroupVos 考勤组列表
     * @param festivalSetting    节假日设置映射
     * @param hisFestivalSetting 历史节假日设置实体
     **/
    void addHolidayDailyRule(Date start, Date end, List<AttendanceGroup> attendanceGroupVos, AttendanceFestivalRules festivalSetting, AttendanceFestivalRules hisFestivalSetting, List<String> newUserIds, List<String> oldUserIds);

    /**
     * 为自助调度设置时间表
     *
     * @param shiftId 班次ID，用于标识特定的班次
     * @return 如果设置成功，返回确认信息；否则返回错误信息
     * @throws HandleException 如果设置过程中发生错误，则抛出此异常
     */
    String setSchedulesForSelfSchedules(String shiftId) throws HandleException;

    /**
     * 更新班次配置时设置时间表
     *
     * @param groupId 组ID，用于标识需要更新配置的组
     * @param mark 标记，用于指示更新的版本或状态
     * @param periodConfigs 时段配置列表，包含需要更新的班次配置信息
     * @return 如果设置成功，返回受影响的行数或状态码；否则返回错误信息
     * @throws HandleException 如果设置过程中发生错误，则抛出此异常
     */
    Integer setSchedulesForShiftConfigUpdate(String groupId, Integer mark, List<PeriodConfig> periodConfigs, List<AttendanceShiftSettingPeriodEntity> periodEntities) throws HandleException;

    /**
     * 设置排班
     *
     * @param schedulesSets 排班设置列表
     * @return 排班结果字符串
     * @throws HandleException 处理异常
     **/
    String setSchedules(List<SchedulesSetDto> schedulesSets) throws HandleException;

    /**
     * 初始化固定排班规则。
     *
     * @param tenantId 租户ID
     */
    void initFixedScheduleRule(String tenantId);

    void clearGroupRule(String groupId, List<String> userIds);

    /**
     * 清除考勤组用户集合的考勤规则
     *
     * @param groupId 考勤组ID
     * @param userIds userId集合
     */
    void clearGroupRule(String groupId, List<String> userIds, Date departTime, String tenantId);

    /**
     * 申请验证的日规则处理
     *
     * @param applyParam 申请参数类
     */
    List<DailyRuleResultVo> applyVerifyHandle(ApplyParam applyParam) throws HandleException;

    /**
     * 申请的日规则处理
     *
     * @param applyParam 申请参数类
     */
    String applyDailyRuleHandle(ApplyParam applyParam) throws HandleException;

    /**
     * 借调申请日规则处理
     *
     * @param userIds       借调用户id集合
     * @param fromGroupId   原考勤组id
     * @param toGroupId     借调考勤组id
     * @param start         开始时间
     * @param end           结束时间
     * @param departureTime 离岗时间
     * @param backTime      回岗时间
     * @param tenantId
     */
    List<DailyRuleResultVo> secondmentDailyRuleHandle(List<String> userIds, String fromGroupId, String toGroupId, Date start, Date end, Date departureTime, Date backTime, String tenantId) throws HandleException;

    /**
     * 查询用户当日排班信息
     *
     * @param groupId 群组ID，用于识别哪个群组的考勤规则需要查询
     * @param userId  用户ID，指定查询考勤规则的用户
     * @param day     日期，指定查询考勤规则的具体日期
     * @return 返回一个包含考勤规则的列表，这些规则适用于指定用户和日期
     */
    List<FtbAttendanceDailyRule> getAttendanceDayRulesForStatistic(String groupId, String userId, Date day);

    /**
     * 获取用户当日可打卡时间范围
     * 开始时间：如果前一天有夸日班次，开始时间就是夸日班次的下班缺卡时间，没有就是当日的00:00:00
     * 结束时间：当日最后一个班次的下班缺卡时间
     *
     * @param userId 用户ID，指定查询考勤规则的用户
     * @param day    日期，指定查询考勤规则的具体日期
     * @return 返回一个包含开始时间和结束时间的对象
     */
    DayClockRange getDayClockRange(String userId, Date day);

    /**
     * 获取当天最后一个班次的下班缺卡时间
     *
     * @param userId 用户ID，指定查询考勤规则的用户
     * @param day    日期，指定查询考勤规则的具体日期
     * @return 返回一个Date对象，表示当天最后一个班次的下班缺卡时间
     */
    Date getDayEndRuleDeletionDate(String userId, Date day);

    List<String> userIsSchedulingOrdinary(List<String> organizeIds);

    boolean hasRuleByUserIdAndTime(String userId, Date start, Date end);

    /**
     * 获取用户当天外出/出差批次号
     *
     * @param userId       用户ID，指定查询考勤规则的用户
     * @param groupId       考勤组ID
     * @param day          日期，指定查询考勤规则的具体日期
     * @param typeEnumList     出勤类型
     * @return 返回一个包含外出/出差批次号的列表
     */
    List<OutOrBusApproveVo> getUserDayBusAndOutInfo(String userId, String groupId, Date day, List<AttendanceTypeEnum> typeEnumList);

    /**
     * 获取用户外出出差次数
     *
     * @param userIds      用户ID，指定查询考勤规则的用户
     * @param groupIdList  考勤组ID集合
     * @param startDate    开始时间
     * @param endDate      结束时间
     * @param businessTrip 出勤类型
     * @return Integer
     */
    Map<String, Integer> getUserBusAndOutCount(List<String> userIds, List<String> groupIdList, Date startDate, Date endDate, AttendanceTypeEnum businessTrip);

    /**
     * 用户是否排班
     *
     * @param userId 用户ID
     * @return boolean true: 是  false: 否
     */
    boolean userIsScheduling(String userId);

    List<SchedulesV2Vo> schedulesImport(SchedulesImportDto schedulesImportDto) throws IOException;


    Map<String, BigDecimal> getUserPublicHoliday(String yearMonth, List<String> userIds);

    /**
     * 获取最早排班时间
     *
     * @return Date
     */
    Date getEarliestSchedulingDate(Date start);

    boolean hasLinearRulesByPeriod(String userId, Date start, Date end);

    /**
     * 设置划线排班
     *
     * @param configDto 划线排班配置DTO
     * @return 处理结果
     * @throws HandleException 处理异常
     */
    String setLineDrawingSchedules(LineDrawingSchedulesConfigDto configDto) throws HandleException;

    boolean queryLineSchedulingExist(LineDrawingSchedulesConfigDto configDto);

    void lineSchedulesExport(String groupId, String workGroupId, String month, List<String> userIdList);

    void lineSchedulesImport(SchedulesImportDto schedulesImportDto) throws IOException;

    List<LineSchedulesVo> getLineSchedulesList(String groupId, String workGroupId, List<String> dayList, List<String> finalUserIdList);
    /**
     * 查询单个用户是否划线排班
     * @param userId
     * @param start
     * @param end
     * @return
     */
    boolean isLineScheduleByUserId(String userId, Date start, Date end);

    FtbAttendanceLineSchedulingConfig lineSchedulesConfigFilter(String groupId, List<String> userIds);

    @Nullable FtbAttendanceLineSchedulingConfig getFtbAttendanceLineSchedulingConfig(List<String> userIds, FtbAttendanceLineSchedulingConfig lineSchedulingConfig);

    /**
     * 获取排班
     * @param finalStartDate 开始日期
     * @param finalEndDate   结束日期
     * @return 排班列表
     */
    List<CreateDayStatistics> getDayRuleByMonth(LocalDate finalStartDate, LocalDate finalEndDate);

    /**
     * 查询指定用户指定日期的班次信息，合并展示普班和请假时段
     * 请假信息作为班次信息的补充，当班次被请假覆盖时，根据覆盖情况展示时段
     *
     * @param userId 用户ID
     * @param queryDate 查询日期
     * @return 用户指定日期时段信息（包含普班和请假）
     */
    UserDayShiftInfoVo getUserDayShiftInfo(String userId, Date queryDate);

    /**
     * 统一排班接口（支持固定排班和划线排班）
     *
     * @param dto 统一排班DTO
     * @return 处理结果消息
     * @throws HandleException 处理异常
     */
    String setUnifiedSchedules(UnifiedSchedulesDto dto) throws HandleException;

    /**
     * 查询考勤组近 90 天历史排班：按自然日返回班次及岗位人数；划线排班一人一条，人数为 1
     *
     * @param groupId 考勤组 id
     * @return 每日营业额（预留）、班次及岗位人数列表
     */
    List<DayShiftRevenueStatVo> getGroupShiftHistory90Days(String groupId);

    /**
     * 按自然日查询考勤组所属门店营业额预估
     *
     * @param groupId   考勤组 id
     * @param startTime 开始日期（含）
     * @param endTime   结束日期（含）
     * @return 按日营业额预估列表
     */
    List<DayReceivableRevenueVo> listReceivableRevenueByDay(String groupId, Date startTime, Date endTime);
}
