package com.ssm.manage.service.impl;

import com.ssm.core.utils.SnowflakeUtil;
import com.ssm.manage.entity.AbsenteeismRecord;
import com.ssm.manage.entity.JobContentList;
import com.ssm.manage.entity.StaffTemporary;
import com.ssm.manage.mapper.AbsenteeismRecordMapper;
import com.ssm.manage.mapper.JobContentListMapper;
import com.ssm.manage.mapper.StaffTemporaryMapper;
import com.ssm.manage.service.AbsenteeismRecordService;
import com.ssm.manage.utils.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import javax.validation.constraints.NotNull;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class AbsenteeismRecordImpl implements AbsenteeismRecordService {
    private final AbsenteeismRecordMapper absenteeismRecordMapper;
    private final StaffTemporaryMapper staffTemporaryMapper;
    private final JobContentListMapper jobContentListMapper;

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public void generateRecords(@NotNull Date date) {
        /**
         * 使用身份证是为了确保不同类型之间的记录关联正确。
         * 因为使用员工编号的话，员工编号可以被修改，容易导致记录之间关联不到的情况；
         * 身份证一般是固定的，不容易被修改，使用身份证进行关联能确保记录之间关联的准确性。
         */

        // 获取指定日期的需打卡人员身份证
        List<StaffTemporary> staffTemporaries = staffTemporaryMapper.selectNotLeaving(date);
        Set<String> identityCards = staffTemporaries.stream().map(StaffTemporary::getIdentityCard)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        log.debug("需要打卡人员的身份证: {}", identityCards);

        // 没有人员需要打卡直接返回
        if (CollectionUtils.isEmpty(staffTemporaries)) {
            return;
        }

        // 获取指定日期的已打卡记录
        Example jclExample = new Example(JobContentList.class);
        Example.Criteria jclExampleCriteria = jclExample.createCriteria();
        jclExampleCriteria.andIn("identityCard", identityCards)
                .andLike("checkTime", dateFormat.format(date) + "%");
        List<JobContentList> jobContentLists = jobContentListMapper.selectByExample(jclExample);
        // 获取已打卡人员身份证
        Set<String> clockedInIdentityCards = jobContentLists.stream().map(JobContentList::getIdentityCard)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        log.debug("已打卡人员身份证: {}", clockedInIdentityCards);

        // 根据需打卡和已打卡计算差集（也就是计算未打卡人员）
        // 由于没有准确的排班，未打卡人员直接按照在职时间范围进行计算，只要在在职时间内但是没有打卡记录的，一律算未打卡
        HashSet<String> diffs = new HashSet<>(identityCards);
        diffs.removeAll(clockedInIdentityCards);
        log.debug("未打卡人员身份证: {}", diffs);

        // 没有未打卡人员，直接返回
        if (CollectionUtils.isEmpty(diffs)) {
            return;
        }

        // 获取未打卡人员的入职信息
        List<StaffTemporary> absenteeismStaffs = staffTemporaries.stream().filter(staffTemporary -> {
            String identityCard = staffTemporary.getIdentityCard();
            return diffs.contains(identityCard);
        }).collect(Collectors.toList());

        Date absenteeismTime = DateUtils.getDateByTime(date, 9);

        // 生成对应人员的缺勤记录
        List<AbsenteeismRecord> absenteeismRecords = absenteeismStaffs.stream().map(staffTemporary -> {
            AbsenteeismRecord absenteeismRecord = new AbsenteeismRecord();

//            absenteeismRecord.setStaffNum(staffTemporary.getStaffNum());
//            absenteeismRecord.setStaffName(staffTemporary.getStaffName());
//            absenteeismRecord.setSex(staffTemporary.getSex());
//            absenteeismRecord.setIdentityCard(staffTemporary.getIdentityCard());
//            absenteeismRecord.setStoreId(staffTemporary.getStoreId());
//            absenteeismRecord.setRegionId(staffTemporary.getRegionId());
            // 上方注释的代码，由于字段名和类型都一致，所以直接使用对象值拷贝，减少代码量
            BeanUtils.copyProperties(staffTemporary, absenteeismRecord);

            absenteeismRecord.setId(new SnowflakeUtil(new Random().nextInt(1023)).nextId());
            // 设置外键，但是需要注意打卡记录 job_content_list 的 staff_id 是黑名单表 black_list的id
            absenteeismRecord.setStaffTempId(staffTemporary.getId());
            absenteeismRecord.setAttendanceTime(absenteeismTime);
            return absenteeismRecord;
        }).collect(Collectors.toList());

        // 批量插入，失败时不抛异常，使用 log 进行记录
        absenteeismRecords.forEach(absenteeismRecord -> {
            int i = absenteeismRecordMapper.insert(absenteeismRecord);
            if (i != 1) {
                log.error("插入失败: {}", absenteeismRecord);
            }
        });
    }
}
