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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.attendance.dto.AttendancePunchPageDTO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendancePunchPageVO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceRulesVO;
import cn.iocoder.yudao.module.system.controller.admin.groupshift.vo.AttendanceGroupShiftVO;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingRespVO;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.scheduling.AttendanceSchedulingMapper;
import cn.iocoder.yudao.module.system.service.attendance.AttendanceService;
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService;
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.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.SCHEDULING_NOT_EXISTS;

/**
 * 排班制考勤设置 Service 实现类
 *
 * @author 艾楷
 */
@Service("scheduling")
@Validated
public class AttendanceSchedulingServiceImpl implements AttendanceSchedulingService, PunchService {

    @Resource
    private AttendanceSchedulingMapper schedulingMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private AttendanceGroupShiftService attendanceGroupShiftService;
    @Resource
    @Lazy
    private AttendanceService attendanceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long batchCreateOrUpdate(Long attendanceGroupId, List<AttendanceSchedulingSaveReqVO> createReqVO) {
        Long groupId = null;
        List<AttendanceSchedulingDO> oldList = schedulingMapper.selectList(new LambdaQueryWrapper<AttendanceSchedulingDO>().eq(AttendanceSchedulingDO::getAttendanceGroupId, attendanceGroupId));
        List<AttendanceSchedulingDO> scheduling = BeanUtils.toBean(createReqVO, AttendanceSchedulingDO.class);
        // -- 如果久的没数据 则直接新增
        if (oldList.isEmpty()) {
            groupId = attendanceGroupId;
            schedulingMapper.insertBatch(scheduling);
        } else {
            //如果久的有数据则区分下
            // 插入
            List<AttendanceSchedulingDO> saveList = scheduling.stream().filter(a -> a.getId() == null).collect(Collectors.toList());
            List<AttendanceSchedulingDO> editList = scheduling.stream().filter(a -> a.getId() != null).collect(Collectors.toList());
            if (!saveList.isEmpty()) {
                groupId = attendanceGroupId;
                schedulingMapper.insertBatch(saveList);
            }
            //获取到当天是第几天
            String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + attendanceGroupId;
            String indexDayObj = stringRedisTemplate.opsForValue().get(schedulingKey);
            if (!editList.isEmpty()) {
                Map<Integer, AttendanceSchedulingDO> map = oldList.stream().collect(Collectors.toMap(AttendanceSchedulingDO::getIndexDay, a -> a));
                for (AttendanceSchedulingDO attendanceSchedulingDO : editList) {
                    AttendanceSchedulingDO item = map.get(attendanceSchedulingDO.getIndexDay());
                    boolean eqFlag = BeanUtil.isCommonFieldsEqual(attendanceSchedulingDO, item, "createTime", "updateTime", "creator", "updater", "deleted");
                    // -- 判断当天是修改的数据是否是当天 - 如果是的话则需要更新
                    if (!eqFlag && (StrUtil.isNotEmpty(indexDayObj) && Integer.parseInt(indexDayObj) == attendanceSchedulingDO.getIndexDay())) {
                        groupId = attendanceGroupId;
                        break;
                    }
                }
                schedulingMapper.updateBatch(editList);
            }
            // -- 需要删除的 -
            List<Long> oldIds = oldList.stream().map(AttendanceSchedulingDO::getId).collect(Collectors.toList());
            List<Long> newIds = editList.stream().map(AttendanceSchedulingDO::getId).collect(Collectors.toList());
            List<Long> delIds = new ArrayList<>(CollectionUtil.subtract(oldIds, newIds));
            if (!delIds.isEmpty()) {
                schedulingMapper.deleteBatchIds(delIds);
                groupId = attendanceGroupId;
            }
        }
        return groupId;
    }

    @Override
    public void updateScheduling(AttendanceSchedulingSaveReqVO updateReqVO) {
        // 校验存在
        validateSchedulingExists(updateReqVO.getId());
        // 更新
        AttendanceSchedulingDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceSchedulingDO.class);
        schedulingMapper.updateById(updateObj);
    }

    @Override
    public void deleteScheduling(Long id) {
        // 校验存在
        validateSchedulingExists(id);
        // 删除
        schedulingMapper.deleteById(id);
    }

    private void validateSchedulingExists(Long id) {
        if (schedulingMapper.selectById(id) == null) {
            throw exception(SCHEDULING_NOT_EXISTS);
        }
    }

    @Override
    public AttendanceSchedulingDO getScheduling(Long id) {
        return schedulingMapper.selectById(id);
    }

    @Override
    public PageResult<AttendanceSchedulingDO> getSchedulingPage(AttendanceSchedulingPageReqVO pageReqVO) {
        return schedulingMapper.selectPage(pageReqVO);
    }

    @Override
    public AttendanceSchedulingDO getSchedulingByIndexDay(Long attendanceGroupId, Integer indexDay) {
        List<AttendanceSchedulingDO> list = schedulingMapper.selectList(new LambdaQueryWrapper<AttendanceSchedulingDO>()
                .eq(AttendanceSchedulingDO::getAttendanceGroupId, attendanceGroupId)
                .eq(AttendanceSchedulingDO::getIndexDay, indexDay));
        if (!list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    @Override
    public List<AttendanceSchedulingDO> getSchedulingByGroupId(Long attendanceGroupId) {
        return schedulingMapper.selectList(new LambdaQueryWrapper<AttendanceSchedulingDO>()
                .eq(AttendanceSchedulingDO::getAttendanceGroupId, attendanceGroupId));
    }

    @Override
    public Integer getCycleDay(Long attendanceGroupId) {
        return Math.toIntExact(schedulingMapper.selectCount(new LambdaQueryWrapper<AttendanceSchedulingDO>()
                .eq(AttendanceSchedulingDO::getAttendanceGroupShiftId, attendanceGroupId)));
    }

    @Override
    public Map<Long, Map<String, Object>> countGroupId() {
        return schedulingMapper.countGroupId();
    }

    @Override
    public Map<Long, Long> getGroupToShiftIdMap(List<AttendanceGroupDO> schedulingList, LocalDateTime localDateTime) {
        Map<Long, Long> map = new HashMap<>();
        for (AttendanceGroupDO activationGroup : schedulingList) {
            //获取到当天是第几天
            String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + activationGroup.getId();
            String indexDayObj = stringRedisTemplate.opsForValue().get(schedulingKey);
            if (StrUtil.isEmpty(indexDayObj)) {
                // 当作第一天来
                indexDayObj = Constants.ONE_STR;
                stringRedisTemplate.opsForValue().set(schedulingKey, indexDayObj);
            }
            Integer indexDay = Integer.valueOf(indexDayObj);
            AttendanceSchedulingDO attendanceSchedulingDO = this.getSchedulingByIndexDay(activationGroup.getId(), indexDay);
            // -- 当前没有班次 - 不需要考勤
            if (attendanceSchedulingDO == null || Constants.ONE.equals(attendanceSchedulingDO.getRestFlag()) || attendanceSchedulingDO.getAttendanceGroupShiftId() == null) {
                continue;
            }
            map.put(activationGroup.getId(), attendanceSchedulingDO.getAttendanceGroupShiftId());
        }
        return map;
    }


    @Override
    public List<AttendanceSchedulingRespVO> getListByGroupId(Long attendanceGroupId) {
        List<AttendanceSchedulingRespVO> respVOS = new ArrayList<>();
        List<AttendanceSchedulingDO> selectedList = schedulingMapper.selectList(new LambdaQueryWrapper<AttendanceSchedulingDO>()
                .eq(attendanceGroupId != null, AttendanceSchedulingDO::getAttendanceGroupId, attendanceGroupId));
        if (!selectedList.isEmpty()) {
            respVOS = BeanUtils.toBean(selectedList, AttendanceSchedulingRespVO.class);
            List<Long> attendanceGroupShiftIds = selectedList.stream().map(AttendanceSchedulingDO::getAttendanceGroupShiftId).filter(Objects::nonNull).collect(Collectors.toList());

            List<AttendanceGroupShiftVO> vos = attendanceGroupShiftService.listByShiftIds(attendanceGroupShiftIds);
            Map<Long, AttendanceGroupShiftVO> map = vos.stream().collect(Collectors.toMap(AttendanceGroupShiftVO::getId, a -> a));
            for (AttendanceSchedulingRespVO respVO : respVOS) {
                respVO.setShiftVO(map.get(respVO.getAttendanceGroupShiftId()));
            }
        }
        return respVOS;
    }

    @Override
    public List<Long> getUseGroupIds(Long shiftId) {
        return schedulingMapper.getUseGroupIds(shiftId);
    }

    @Override
    public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) {
        AttendancePunchPageVO vo = new AttendancePunchPageVO();
        AttendanceGroupDO activationGroup = dto.getActivationGroup();
        vo.setActivationGroup(activationGroup);
        //获取到当天是第几天
        String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + activationGroup.getId();
        String indexDayObj = stringRedisTemplate.opsForValue().get(schedulingKey);
        if (StrUtil.isEmpty(indexDayObj)) {
            // 当作第一天来
            indexDayObj = Constants.ONE_STR;
        }
        Integer indexDay = Integer.valueOf(indexDayObj);
        AttendanceSchedulingDO attendanceSchedulingDO = this.getSchedulingByIndexDay(activationGroup.getId(), indexDay);
        // -- 当前没有班次 - 不需要考勤
        if (attendanceSchedulingDO == null || Constants.ONE.equals(attendanceSchedulingDO.getRestFlag()) || attendanceSchedulingDO.getAttendanceGroupShiftId() == null) {
            return vo.setTodayNeedAttendance(Constants.FALSE);
        }
        vo.setAttendanceGroupShiftId(attendanceSchedulingDO.getAttendanceGroupShiftId());
        attendanceService.calculatePunch(dto, vo);
        vo.setAttendanceGroupId(activationGroup.getId());
        vo.setUser(dto.getUser());
        return vo;
    }

    @Override
    public List<AttendanceRulesVO.WorkRules> getRule(Long attendanceGroupId) {
        List<AttendanceSchedulingDO> list = this.getSchedulingByGroupId(attendanceGroupId);

        List<AttendanceRulesVO.WorkRules> rules = new ArrayList<>();
        List<Long> attendanceGroupShiftIds = list.stream().map(AttendanceSchedulingDO::getAttendanceGroupShiftId).filter(Objects::nonNull).collect(Collectors.toList());
        List<AttendanceGroupShiftVO> vos = attendanceGroupShiftService.listByShiftIds(attendanceGroupShiftIds);
        Map<Long, AttendanceGroupShiftVO> map = vos.stream().collect(Collectors.toMap(AttendanceGroupShiftVO::getId, a -> a));
        //获取到当天是第几天
        String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + attendanceGroupId;
        String indexDayObj = stringRedisTemplate.opsForValue().get(schedulingKey);

        for (AttendanceSchedulingDO attendanceSchedulingDO : list) {
            AttendanceRulesVO.WorkRules workRules = new AttendanceRulesVO.WorkRules();
            String indexDay = "第" + attendanceSchedulingDO.getIndexDay() + "天";
            workRules.setIndexStr(indexDay);
            workRules.setFlag(false);
            if (StrUtil.isNotEmpty(indexDayObj) && Integer.parseInt(indexDayObj) == attendanceSchedulingDO.getIndexDay()) {
                workRules.setFlag(true);
            }
            if (attendanceSchedulingDO.getAttendanceGroupShiftId() == null) {
                workRules.setRule("休息");
            } else {
                AttendanceGroupShiftVO attendanceGroupShiftVOS = map.get(attendanceSchedulingDO.getAttendanceGroupShiftId());
                List<String> itemRule = new ArrayList<>();
                for (AttendanceGroupShiftItemDO attendanceGroupShiftItemDO : attendanceGroupShiftVOS.getAttendanceGroupShiftItemDOList()) {
                    itemRule.add(attendanceGroupShiftItemDO.getBeginTime() + "-" + attendanceGroupShiftItemDO.getEndTime());
                }
                String rule = String.join(",", itemRule);
                workRules.setRule(rule);
            }
            rules.add(workRules);
        }
        return rules;
    }

}