package cn.iocoder.yudao.module.system.service.attendance.groupshift;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.AttendanceGroupShiftPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.AttendanceGroupShiftSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.AttendanceGroupShiftVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.fixed.AttendanceFixedDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.fixed.AttendanceFixedMapper;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.groupshift.AttendanceGroupShiftMapper;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.groupshiftitem.AttendanceGroupShiftItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.punchrecord.AttendancePunchRecordMapper;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.scheduling.AttendanceSchedulingMapper;
import cn.iocoder.yudao.module.system.service.attendance.group.AttendanceGroupService;
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 考勤组班次 Service 实现类
 *
 * @author 艾楷
 */
@Service
@Validated
public class AttendanceGroupShiftServiceImpl implements AttendanceGroupShiftService {

    @Resource
    private AttendanceGroupShiftMapper groupShiftMapper;
    @Resource
    private AttendanceGroupShiftItemMapper groupShiftItemMapper;
    @Resource
    private AttendanceFixedMapper attendanceFixedMapper;
    @Resource
    private AttendanceSchedulingMapper attendanceSchedulingMapper;
    @Resource
    @Lazy // 避免依赖循环
    private AttendanceGroupService attendanceGroupService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    @Lazy
    private AttendancePunchRecordMapper attendancePunchRecordMapper;
    @Resource
    @Lazy
    private AttendancePunchRecordService attendancePunchRecordService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createGroupShift(AttendanceGroupShiftSaveReqVO createReqVO) {
        // 插入
        AttendanceGroupShiftDO groupShift = BeanUtils.toBean(createReqVO, AttendanceGroupShiftDO.class);
        // -- 插入子表
        List<AttendanceGroupShiftItemDO> items = BeanUtils.toBean(createReqVO.getItems(), AttendanceGroupShiftItemDO.class);
        items = insertSpanDay(items);
        long count = items.stream().filter(a -> a.getNextDayFlag() == 1).count();
        groupShift.setNextDayFlag(count > 0 ? 1 : 0);
        groupShiftMapper.insert(groupShift);
        for (AttendanceGroupShiftItemDO item : items) {
            item.setKqAttendanceGroupShiftId(groupShift.getId());
        }
        groupShiftItemMapper.insertBatch(items);
        // 返回
        return groupShift.getId();
    }

    private static List<AttendanceGroupShiftItemDO> insertSpanDay(List<AttendanceGroupShiftItemDO> items) {
        //先根据items中的level进行升序排序
        items = items.stream().sorted(Comparator.comparingInt(AttendanceGroupShiftItemDO::getLevel)).collect(Collectors.toList());
        AttendanceGroupShiftItemDO first = CollectionUtil.getFirst(items);
        //获取明天时间
        LocalDateTime tomorrow = LocalDateTimeUtil.offset(LocalDateTime.now(), 1, ChronoUnit.DAYS);
        LocalDateTime today = LocalDateTime.now();
        // -- 开始时间 - 这个肯定是当天的
        Date todayStartTime = DateUtils.buildHHmmTime(first.getBeginTime(), today);
        int nextDay = 0;
        for (AttendanceGroupShiftItemDO item : items) {
            Date beginTime = DateUtils.buildHHmmTime(item.getBeginTime(), nextDay == 0 ? today : tomorrow);
            Date endTime = DateUtils.buildHHmmTime(item.getEndTime(), nextDay == 0 ? today : tomorrow);
            int nextDayFlag = nextDay;
            if (nextDay == 0) {
                if (beginTime.getTime() < todayStartTime.getTime()) {
                    nextDayFlag = 2;
                    nextDay = 1;
                }
                if (endTime.getTime() < todayStartTime.getTime()) {
                    nextDayFlag = nextDayFlag == 0 ? 2 : 1;
                    nextDay = 1;
                }
            }
            item.setNextDayFlag(nextDayFlag);
        }
        if (nextDay == 1) {
            AttendanceGroupShiftItemDO last = CollectionUtil.getLast(items);
            Date endTime = DateUtils.buildHHmmTime(last.getEndTime(), tomorrow);
            //区间大于1天
            Date deadlineTime = DateUtils.buildHHmmTime(first.getBeginTime(), tomorrow);
            if (endTime.getTime() > deadlineTime.getTime()) {
                throw exception(THE_ATTENDANCE_INTERVAL_SHALL_NOT_EXCEED24_HOURS);
            }
        }
        return items;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> updateGroupShift(AttendanceGroupShiftSaveReqVO updateReqVO) {
        // 校验存在
        validateGroupShiftExists(updateReqVO.getId());
        // 更新
        AttendanceGroupShiftDO attendanceGroupShiftDO = BeanUtils.toBean(updateReqVO, AttendanceGroupShiftDO.class);
        // -- 插入子表
        List<AttendanceGroupShiftItemDO> items = BeanUtils.toBean(updateReqVO.getItems(), AttendanceGroupShiftItemDO.class);
        items = insertSpanDay(items);
        List<AttendanceGroupShiftItemDO> oldItems = groupShiftItemMapper.selectList(new LambdaQueryWrapper<AttendanceGroupShiftItemDO>()
                .eq(AttendanceGroupShiftItemDO::getKqAttendanceGroupShiftId, updateReqVO.getId()));

        // -- 查询下有没有考勤组在用这个班次 - 如果有的话就要考虑更新 是立即更新 还是 次日更新
        List<Long> useGroupIds = attendanceGroupService.getUseGroupIds(updateReqVO.getId());

        boolean flag = false;
        if (CollectionUtil.isEmpty(oldItems)) {
            groupShiftItemMapper.insertBatch(items);
            flag = true;
        } else {
            Map<Long, AttendanceGroupShiftItemDO> map = oldItems.stream().collect(Collectors.toMap(AttendanceGroupShiftItemDO::getId, a -> a));
            //新增
            List<AttendanceGroupShiftItemDO> saveList = items.stream().filter(a -> a.getId() == null).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(saveList)) {
                for (AttendanceGroupShiftItemDO attendanceGroupShiftItemDO : saveList) {
                    attendanceGroupShiftItemDO.setKqAttendanceGroupShiftId(updateReqVO.getId());
                }
                groupShiftItemMapper.insertBatch(saveList);
                flag = true;
            }
            //修改
            List<AttendanceGroupShiftItemDO> editList = items.stream().filter(a -> a.getId() != null).collect(Collectors.toList());
            //先查询一手 - 完了后判断下和修改的值有什么不同
            if (CollectionUtil.isNotEmpty(editList)) {
                groupShiftItemMapper.updateBatch(editList);
                for (AttendanceGroupShiftItemDO attendanceGroupShiftItemDO : editList) {
                    AttendanceGroupShiftItemDO item = map.get(attendanceGroupShiftItemDO.getId());
                    boolean eqFlag = BeanUtil.isCommonFieldsEqual(attendanceGroupShiftItemDO, item, "createTime", "updateTime", "creator", "updater", "deleted");
                    if (!eqFlag) {
                        flag = true;
                        break;
                    }
                }
            }
            //删除
            // -- 需要删除的 -
            List<Long> oldIds = oldItems.stream().map(AttendanceGroupShiftItemDO::getId).collect(Collectors.toList());
            List<Long> newIds = editList.stream().map(AttendanceGroupShiftItemDO::getId).collect(Collectors.toList());
            List<Long> delIds = new ArrayList<>(CollectionUtil.subtract(oldIds, newIds));
            if (!delIds.isEmpty()) {
                groupShiftItemMapper.deleteBatchIds(delIds);
                flag = true;
            }
            groupShiftMapper.updateById(attendanceGroupShiftDO);
        }
        // -- 如果班次子表调整了 - 并且有考勤组在使用 - 那么需要更新下明天和后天生成的数据
        if (flag && CollectionUtil.isNotEmpty(useGroupIds)) {
            return useGroupIds;
        }
        return Collections.emptyList();
    }

    @Override
    public void deleteGroupShift(Long id) {
        //查看当前考勤组班次是否在使用 如果是的话 则不允许删除
        Long fixedNum = attendanceFixedMapper.selectCount(new LambdaQueryWrapper<AttendanceFixedDO>().eq(AttendanceFixedDO::getAttendanceGroupShiftId, id));
        Long schedulingNum = attendanceSchedulingMapper.selectCount(new LambdaQueryWrapper<AttendanceSchedulingDO>().eq(AttendanceSchedulingDO::getAttendanceGroupShiftId, id));
        if (fixedNum + schedulingNum > 0) {
            throw exception(GROUP_SHIFT_IN_USE);
        }
        // 校验存在
        validateGroupShiftExists(id);
        // 删除
        groupShiftMapper.deleteById(id);
    }

    private void validateGroupShiftExists(Long id) {
        if (groupShiftMapper.selectById(id) == null) {
            throw exception(GROUP_SHIFT_NOT_EXISTS);
        }
    }

    @Override
    public AttendanceGroupShiftDO getGroupShift(Long id) {
        return groupShiftMapper.selectById(id);
    }

    @Override
    public PageResult<AttendanceGroupShiftDO> getGroupShiftPage(AttendanceGroupShiftPageReqVO pageReqVO) {
        return groupShiftMapper.selectPage(pageReqVO);
    }

    @Override
    public List<AttendanceGroupShiftVO> allList() {
        List<AttendanceGroupShiftDO> dos = groupShiftMapper.selectList();
        return this.buildShift(dos);
    }

    @Override
    public List<AttendanceGroupShiftVO> listByShiftIds(List<Long> listByShiftIds) {
        List<AttendanceGroupShiftDO> dos = groupShiftMapper.selectBatchIds(listByShiftIds);
        return this.buildShift(dos);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void effectiveImmediately(List<Long> groupIds) {
        LocalDateTime now = LocalDateTime.now();
        String time = now.format(Constants.REPO_DATE_FORMAT);
        String yesterdayStr = LocalDateTimeUtil.offset(now, 1, ChronoUnit.DAYS).format(Constants.REPO_DATE_FORMAT);
        // TODO: 2024/6/5 删除当天redis中考勤记录 - 重新计算新的考勤记录到redis 和 数据库中
        for (Long groupId : groupIds) {
            String key = Constants.ATTENDANCE + Constants.UNDERLINE + groupId + Constants.UNDERLINE; // + 时间
            stringRedisTemplate.delete(key + time);
            stringRedisTemplate.delete(key + yesterdayStr);
            // -- 生成新的考勤记录到redis中 - 和数据库中（数据库中会有什么问题？） - 可能会导致两条数据一起更新 旧的数据和新的数据一起更新
            // -- 如果不生成会导致什么问题 - 可能导致考勤组人员缺卡 从而导致统计的时候缺卡统计不到
        }

        // TODO: 2024/6/6 这里如果慢的话可以异步处理
        // -- 删除当前考勤组预留的考勤记录
        attendancePunchRecordMapper.delete(new LambdaQueryWrapper<AttendancePunchRecordDO>()
                .in(AttendancePunchRecordDO::getAttendanceGroupId, groupIds)
                .and(a -> a.eq(AttendancePunchRecordDO::getNextDayFlag, Constants.TRUE)
                        .or()
                        .eq(AttendancePunchRecordDO::getStatus, AttendanceOnTheDayDTO.PUNCH_STATUS_UN_PUNCH))
        );
        // -- 重新计算 - 插入到redis 和数据中
        attendancePunchRecordService.defaultPersistence(groupIds, LocalDateTime.now());
    }

    private List<AttendanceGroupShiftVO> buildShift(List<AttendanceGroupShiftDO> dos) {
        // 拿到ids - 完了后 获取所有的班次子表
        List<Long> ids = dos.stream().map(AttendanceGroupShiftDO::getId).collect(Collectors.toList());
        Map<Long, List<AttendanceGroupShiftItemDO>> map = new HashMap<>();
        if (!ids.isEmpty()) {
            List<AttendanceGroupShiftItemDO> attendanceGroupShiftItemDOS = groupShiftItemMapper.selectList(new LambdaQueryWrapper<AttendanceGroupShiftItemDO>()
                    .in(AttendanceGroupShiftItemDO::getKqAttendanceGroupShiftId, ids));
            map = attendanceGroupShiftItemDOS.stream().collect(Collectors.groupingBy(AttendanceGroupShiftItemDO::getKqAttendanceGroupShiftId));
        }
        List<AttendanceGroupShiftVO> vos = new ArrayList<>();
        for (AttendanceGroupShiftDO item : dos) {
            AttendanceGroupShiftVO attendanceGroupShiftVO = BeanUtils.toBean(item, AttendanceGroupShiftVO.class);
            List<AttendanceGroupShiftItemDO> attendanceGroupShiftItemDOS = map.get(item.getId());
            if (CollectionUtil.isNotEmpty(attendanceGroupShiftItemDOS)) {
                attendanceGroupShiftItemDOS = attendanceGroupShiftItemDOS.stream().sorted(Comparator.comparing(AttendanceGroupShiftItemDO::getLevel)).collect(Collectors.toList());
                attendanceGroupShiftVO.setAttendanceGroupShiftItemDOList(attendanceGroupShiftItemDOS);
            }
            vos.add(attendanceGroupShiftVO);
        }
        return vos;
    }
}