package jnpf.util.attendance;

import cn.hutool.core.collection.CollUtil;
import jnpf.entity.AttendanceGroupUser;
import jnpf.enums.attendance.GroupUserTypeEnum;
import jnpf.model.attendance.vo.SecondmentDateVo;
import jnpf.util.DateDetail;
import jnpf.util.JsonUtil;
import jnpf.util.StringUtil;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 考勤组成员在组 / 借调 / 离组状态判断工具。
 * <p>从 {@link jnpf.attendance.service.impl.AttendanceDailyRuleServiceImpl} 抽取，
 * 供排班、考勤本、统计等模块统一复用。</p>
 */
public final class AttendanceGroupUserStatusUtil {

    /**
     * 查询当日该用户是否存在于当前考勤组（详细状态）。
     *
     * @return -1离组 0未入 1在组 2借调
     */
    public static Integer findUserIsExistsStatusByDay(List<AttendanceGroupUser> users, Date date) {
        Date start = cn.hutool.core.date.DateUtil.beginOfDay(date);
        Date end = cn.hutool.core.date.DateUtil.endOfDay(date);
        return findUserIsExistsByDay(users, start, end);
    }

    /**
     * 查询当日该用户是否存在于当前考勤组（简化：1存在，0不存在）。
     */
    public static Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date date) {
        return findUserIsExistsStatusByDay(users, date) != 1 ? 0 : 1;
    }

    /**
     * 查询时间范围内该用户是否存在于当前考勤组。
     *
     * @return -1离组 0未入 1在组 2借调
     */
    public static Integer findUserIsExistsByDay(List<AttendanceGroupUser> users, Date start, Date end) {
        if (CollUtil.isEmpty(users)) {
            return 0;
        }
        boolean isInGroup = users.stream().anyMatch(user -> Objects.isNull(user.getRemoveTime())
                ? Boolean.TRUE
                : user.getRemoveTime().compareTo(start) > 0);
        if (!isInGroup) {
            return -1;
        }
        isInGroup = users.stream().anyMatch(user -> user.getUserGroupType() == 2
                ? Boolean.TRUE
                : Objects.isNull(user.getCreatorTime()) ? Boolean.FALSE : end.compareTo(user.getCreatorTime()) >= 0);
        if (!isInGroup) {
            return 0;
        }
        boolean isInSecondment = users.stream()
                .filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.CUR.getCode()))
                .anyMatch(user -> {
                    List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
                    if (CollUtil.isEmpty(secondmentDateVos)) {
                        return Boolean.FALSE;
                    }
                    return secondmentDateVos.stream().anyMatch(dateVo ->
                            DateDetail.checkTimeBetween(start, dateVo.getStartTime(), dateVo.getEndTime())
                                    && DateDetail.checkTimeBetween(end, dateVo.getStartTime(), dateVo.getEndTime()));
                });
        List<AttendanceGroupUser> borrowUsers = users.stream()
                .filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.BORROW.getCode()))
                .collect(Collectors.toList());
        boolean isSecondment = borrowUsers.stream().anyMatch(user -> {
            List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
            if (CollUtil.isEmpty(secondmentDateVos)) {
                return Boolean.FALSE;
            }
            return Objects.nonNull(getSecondmentType(secondmentDateVos, start, end));
        });
        if (users.size() == borrowUsers.size() && !isSecondment) {
            if (borrowUsers.stream().allMatch(user -> {
                List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
                return secondmentDateVos.stream().allMatch(vo -> vo.getEndTime().before(start));
            })) {
                return -1;
            }
            return 0;
        }
        return !isInSecondment & (CollUtil.isEmpty(borrowUsers) ? Boolean.TRUE : !isSecondment) ? 1 : 2;
    }

    /**
     * 批量判断用户在某时间范围内是否在组（简化：1存在，0不存在）。
     */
    public static Map<String, Integer> findUserIsExistsByUserList(List<AttendanceGroupUser> users, Date start, Date end) {
        Map<String, List<AttendanceGroupUser>> userListMap = users.stream()
                .collect(Collectors.groupingBy(AttendanceGroupUser::getUserId));
        Map<String, Integer> map = new HashMap<>();
        for (Map.Entry<String, List<AttendanceGroupUser>> user : userListMap.entrySet()) {
            Integer existStatus = isExistStatus(user.getValue(), start, end);
            map.put(user.getKey(), Objects.equals(existStatus, 1)
                    || Objects.equals(existStatus, 3)
                    || Objects.equals(existStatus, 4) ? 1 : 0);
        }
        return map;
    }

    /**
     * 判断当天内用户的状态：-1已离 0未入 1正常 2全天被借调 3部分被借调 4借调。
     */
    public static Integer isExistStatus(List<AttendanceGroupUser> users, Date date) {
        return isExistStatus(users, date, cn.hutool.core.date.DateUtil.endOfDay(date));
    }

    /**
     * 判断时间范围内用户的状态：-1已离 0未入 1正常 2全天被借调 3部分被借调 4借调。
     */
    public static Integer isExistStatus(List<AttendanceGroupUser> users, Date start, Date end) {
        if (CollUtil.isEmpty(users)) {
            return 0;
        }
        List<AttendanceGroupUser> borrowUsers = users.stream()
                .filter(user -> Objects.equals(user.getType(), GroupUserTypeEnum.BORROW.getCode()))
                .collect(Collectors.toList());
        Boolean inSecondment = isInSecondment(users, start, end);
        if (CollUtil.isNotEmpty(borrowUsers)) {
            List<SecondmentDateVo> borrowDates = borrowUsers.stream()
                    .flatMap(user -> StringUtil.isEmpty(user.getTimeJson())
                            ? Stream.empty()
                            : JsonUtil.getJsonToList(user.getTimeJson(), SecondmentDateVo.class).stream())
                    .collect(Collectors.toList());
            List<AttendanceGroupUser> curInBorrowGroup = users.stream()
                    .filter(vo -> borrowUsers.stream().anyMatch(b -> vo.getGroupId().equals(b.getGroupId()))
                            && Objects.equals(vo.getType(), GroupUserTypeEnum.CUR.getCode()))
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(curInBorrowGroup)) {
                int isExist = getIsExist(curInBorrowGroup, start);
                if (isExist != 0) {
                    return isExist;
                }
            }
            if (inSecondment) {
                return 4;
            }
            Date startTime = borrowDates.stream().map(SecondmentDateVo::getStartTime).max(Date::compareTo).orElse(null);
            if (Objects.nonNull(startTime) && end.before(startTime)) {
                return 0;
            }
            return -1;
        }
        List<SecondmentDateVo> allSecondmentDates = users.stream()
                .flatMap(user -> StringUtil.isEmpty(user.getTimeJson())
                        ? Stream.empty()
                        : JsonUtil.getJsonToList(user.getTimeJson(), SecondmentDateVo.class).stream())
                .collect(Collectors.toList());
        if (inSecondment) {
            if (allSecondmentDates.stream().anyMatch(vo ->
                    DateDetail.checkTimeBetween(start, vo.getStartTime(), vo.getEndTime())
                            && DateDetail.checkTimeBetween(end, vo.getStartTime(), vo.getEndTime()))) {
                return 2;
            }
            return 3;
        }
        return getIsExist(users, start);
    }

    /**
     * 是否借调：不管借调还是被借调，只要命中借调时间则返回 true。
     */
    public static Boolean isInSecondment(List<AttendanceGroupUser> users, Date start, Date end) {
        if (CollUtil.isEmpty(users)) {
            return Boolean.FALSE;
        }
        return users.stream().anyMatch(user -> {
            List<SecondmentDateVo> secondmentDateVos = parseSecondmentDates(user.getTimeJson());
            if (CollUtil.isEmpty(secondmentDateVos)) {
                return Boolean.FALSE;
            }
            return Objects.nonNull(getSecondmentType(secondmentDateVos, start, end));
        });
    }

    /**
     * 判断用户在某日是否处于「可维护考勤本」的在组状态。
     * <p>existStatus = 1 视为在组；3（部分被借调）、4（借调）按业务约定也视为在组。</p>
     */
    public static boolean isUserInGroupOnDay(List<AttendanceGroupUser> userGroupUsers, Date day) {
        if (CollUtil.isEmpty(userGroupUsers) || day == null) {
            return false;
        }
        Integer existStatus = isExistStatus(userGroupUsers, day);
        if (Objects.equals(existStatus, 4) || Objects.equals(existStatus, 3)) {
            existStatus = 1;
        }
        return Objects.equals(existStatus, 1);
    }

    /**
     * 获取目标时间范围涉及的借调时段。
     */
    public static SecondmentDateVo getSecondmentType(List<SecondmentDateVo> secondmentDateVos, Date inPoint, Date outPoint) {
        if (CollUtil.isEmpty(secondmentDateVos)) {
            return null;
        }
        for (SecondmentDateVo dateVo : secondmentDateVos) {
            if (DateDetail.checkTimeBetween(inPoint, dateVo.getStartTime(), dateVo.getEndTime())
                    || DateDetail.checkTimeBetween(outPoint, dateVo.getStartTime(), dateVo.getEndTime())
                    || DateDetail.checkTimeBetween(dateVo.getStartTime(), inPoint, outPoint)
                    || DateDetail.checkTimeBetween(dateVo.getEndTime(), inPoint, outPoint)) {
                return dateVo;
            }
        }
        return null;
    }

    private static List<SecondmentDateVo> parseSecondmentDates(String timeJson) {
        return StringUtil.isEmpty(timeJson)
                ? CollUtil.newArrayList()
                : JsonUtil.getJsonToList(timeJson, SecondmentDateVo.class);
    }

    private static int getIsExist(List<AttendanceGroupUser> groupUserVos, Date date) {
        if (Boolean.TRUE.equals(isInSecondment(groupUserVos, date, cn.hutool.core.date.DateUtil.endOfDay(date)))) {
            return 1;
        }
        boolean isIn = groupUserVos.stream()
                .allMatch(user -> cn.hutool.core.date.DateUtil.beginOfDay(user.getCreatorTime()).after(date));
        boolean isOut = groupUserVos.stream().anyMatch(user -> {
            if (Objects.isNull(user.getRemoveTime())) {
                user.setRemoveTime(jnpf.util.DateUtil.dateAddYears(date, 1));
            }
            return DateDetail.checkTimeBetween(date,
                    cn.hutool.core.date.DateUtil.beginOfDay(user.getCreatorTime()),
                    cn.hutool.core.date.DateUtil.endOfDay(user.getRemoveTime()));
        });
        return isIn ? 0 : !isOut ? -1 : 1;
    }
}
