package com.yida.data.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Sets;
import com.yida.data.attendance.dto.ListRecordDTO;
import com.yida.data.attendance.mapper.EduAttendanceRecordMapper;
import com.yida.data.attendance.mapper.EduAttendanceResultMapper;
import com.yida.data.attendance.mapper.EduAttendanceRuleMapper;
import com.yida.data.attendance.mapper.EduAttendanceVerifyDormMapper;
import com.yida.data.attendance.service.EduAttendanceResultService;
import com.yida.data.attendance.service.GenerateAttendanceResultService;
import com.yida.data.attendance.vo.CountVO;
import com.yida.data.attendance.vo.RecordDetailVO;
import com.yida.data.attendance.vo.RuleListVO;
import com.yida.data.common.core.entity.attendance.*;
import com.yida.data.common.core.entity.user.EduStudent;
import com.yida.data.common.core.enums.AttendanceResult;
import com.yida.data.common.service.CommonService;
import com.yida.data.school.dto.leave.ListStudentRequestDetailDTO;
import com.yida.data.school.feign.leave.RemoteLeaveRequestService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@Transactional
@Service
@RequiredArgsConstructor
public class GenerateAttendanceResultServiceImpl implements GenerateAttendanceResultService {

    private final EduAttendanceRuleMapper eduAttendanceRuleMapper;
    private final EduAttendanceRecordMapper eduAttendanceRecordMapper;
    private final EduAttendanceVerifyDormMapper eduAttendanceVerifyDormMapper;
    private final EduAttendanceResultMapper eduAttendanceResultMapper;

    private final CommonService commonService;
    private final RemoteLeaveRequestService remoteLeaveRequestService;

    @Lazy
    @Resource
    private EduAttendanceResultService eduAttendanceResultService;

    private static final ThreadFactory THREAD_FACTORY = ThreadUtil.newNamedThreadFactory("attendance-generate-result", false);

    @Override
    public void generateStudentAttendanceResult(EduAttendanceConfig config, List<EduStudent> schoolStudentList) {
        LocalDateTime now = LocalDateTime.now();
        LocalDate today = now.toLocalDate();
        LocalDate yesterday = today.minusDays(1L);
        // 今天是否是工作日(是否处理今天的记录)
        boolean isDealToday = commonService.isWorkDay(today);
        // 是否需要处理昨晚的宿舍考勤记录
        boolean isDealYesterday = commonService.isWorkDay(yesterday) && config.getDormitoryNightDdl().isBefore(LocalTime.now());
        if (!isDealToday && !isDealYesterday) {
            return;
        }
        // 将要保存的记录
        Set<EduAttendanceResult> saveResult = Sets.newConcurrentHashSet();
        Set<Long> updateNotArrivePush = Sets.newConcurrentHashSet();
        // 查询学校有效的考勤规则
        // 今天的校门规则/宿舍规则
        List<RuleListVO> todayRuleList = eduAttendanceRuleMapper
            .listValidRule(null, today, today.getDayOfWeek().getValue(), null, null, config.getSchoolId());
        // 昨天的宿舍规则
        List<RuleListVO> yesterdayDormRuleList = isDealYesterday ? eduAttendanceRuleMapper
            .listValidRule(null, yesterday, yesterday.getDayOfWeek().getValue(), 1, null, config.getSchoolId()) : null;
        // 按班级进行分组进行遍历
        Map<Long, List<EduStudent>> studentClassGroup = schoolStudentList.stream()
            .collect(Collectors.groupingBy(EduStudent::getClassId));
        // 线程池处理
        ExecutorService es = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), THREAD_FACTORY);
        List<Future> futureList = new ArrayList<>();
        // 每个班启动一个线程
        for (Entry<Long, List<EduStudent>> entry : studentClassGroup.entrySet()) {
            futureList.add(es.submit(() -> {
                    // 实际的考勤日期  走读生为今天  住宿生可能是昨天(生成昨晚的考勤结果)
                    LocalDate attendanceDate = null;
                    List<EduStudent> studentList = entry.getValue();
                    // 校门适用的规则
                    RuleListVO todaySchoolRule = isDealToday ? matchRule(studentList.get(0), todayRuleList, 0) : null;
                    // 今天宿舍适用的规则
                    RuleListVO todayDormRule = isDealToday ? matchRule(studentList.get(0), todayRuleList, 1) : null;
                    // 昨天宿舍适用的规则
                    RuleListVO yesterdayDormRule =
                        isDealYesterday ? matchRule(studentList.get(0), yesterdayDormRuleList, null) : null;
                    // 当前时间分别匹配的考勤时间段
                    EduAttendanceRuleTime todaySchoolTime = null;
                    EduAttendanceRuleTime todayDormTime = null;
                    EduAttendanceRuleTime yesterdayDormTime = null;
                    if (todaySchoolRule != null) {
                        for (EduAttendanceRuleTime ruleTime : todaySchoolRule.getTimeList()) {
                            // 在某一时段结束时间之后
                            if (now.toLocalTime().isAfter(ruleTime.getTime2())) {
                                todaySchoolTime = ruleTime;
                                break;
                            }
                        }
                    }
                    if (todayDormRule != null) {
                        for (EduAttendanceRuleTime ruleTime : todayDormRule.getTimeList()) {
                            if (ruleTime.getType() == 1) {
                                todayDormTime = ruleTime;
                                break;
                            }
                        }
                    }
                    if (yesterdayDormRule != null) {
                        for (EduAttendanceRuleTime ruleTime : yesterdayDormRuleList.get(0).getTimeList()) {
                            if (ruleTime.getType() == 2) {
                                yesterdayDormTime = ruleTime;
                                break;
                            }
                        }
                    }
                    // 生成每一个学生的数据
                    for (EduStudent student : studentList) {
                        EduAttendanceResult result = new EduAttendanceResult();
                        result.setStudentId(student.getId());
                        result.setSchoolId(student.getSchoolId());
                        result.setCampusId(student.getCampusId());
                        result.setGradeId(student.getGradeId());
                        result.setGradeName(student.getGradeName());
                        result.setClassId(student.getClassId());
                        result.setClassName(student.getClassName());
                        result.setDormRoomId(student.getDormRoomId());
                        result.setDormId(student.getDormId());
                        // 该学生有适用的考勤规则
                        EduAttendanceRuleTime time = null;
                        result.setAttendanceDate(today);
                        // 走读生 或者 住宿生也参与上学打卡
                        if (student.getType() == 0 || (todaySchoolRule != null && student.getType() == 1
                            && todaySchoolRule.getDormitoryFlag() == 1)) {
                            if (todaySchoolRule != null) {
                                // 当前在第一段考勤时间之前
                                time = todaySchoolTime;
                                if (time == null) {
                                    continue;
                                }
                                //在每个考勤时间段结束后再进贤统计,结束之前不统计
                                if (time.getTime2Openset() == 1) {
                                    if (LocalTime.now().isBefore(time.getTime2End())) {
                                        continue;
                                    }
                                } else {
                                    if (LocalTime.now().isBefore(time.getTime2())) {
                                        continue;
                                    }
                                }
                                //删除考勤记录时的结果，自此生成结果
                                eduAttendanceResultMapper.delete(Wrappers.lambdaQuery(new EduAttendanceResult())
                                    .eq(EduAttendanceResult::getStudentId, student.getId())
                                    .eq(EduAttendanceResult::getRuleTimeId, time.getId())
                                    .eq(EduAttendanceResult::getAttendanceDate, today));

                                result.setRuleId(todaySchoolRule.getId());
                                result.setRuleTimeId(time.getId());
                                Integer status = null;
                                // 学生是否有请假记录
                                if (remoteLeaveRequestService.existRequest(
                                    ListStudentRequestDetailDTO.builder().studentId(student.getId())
                                        .startTime(today.atTime(time.getTime1()))
                                        .endTime(today.atTime(time.getTime2())).build())
                                    .getData()) {
                                    status = AttendanceResult.STU_REQUEST.getValue();
                                } else {
                                    ListRecordDTO recordDTO = new ListRecordDTO();
                                    recordDTO.setStudentId(student.getId());
                                    recordDTO.setRuleTimeId(time.getId());
                                    recordDTO.setCreateDate(now.toLocalDate());
                                    recordDTO.setZone(0);
                                    List<RecordDetailVO> recordDetailVOS = eduAttendanceRecordMapper.listRecord(recordDTO);
                                    if (CollUtil.isEmpty(recordDetailVOS)) {
                                        //无记录 则为旷课
                                        status = AttendanceResult.STU_NOTARRIVE.getValue();
                                    } else {
//                                    ("考勤结果，-1-宿管添加的考勤记录，0-正常，1-迟到，10-上学未在打卡时间段,2-早退，20-下学未在打卡时间段，3-未到,4-晚归，5-异常晚归，6-旷宿")
                                        //迟到数
                                        List<RecordDetailVO> late = recordDetailVOS.stream()
                                            .filter(recordDetailVO -> 1 == recordDetailVO.getStatus()).collect(Collectors.toList());
                                        //上学未在打卡时间段
                                        List<RecordDetailVO> startNotIn = recordDetailVOS.stream()
                                            .filter(recordDetailVO -> 10 == recordDetailVO.getStatus()).collect(Collectors.toList());
                                        //早退
                                        List<RecordDetailVO> leave = recordDetailVOS.stream()
                                            .filter(recordDetailVO -> 2 == recordDetailVO.getStatus()).collect(Collectors.toList());
                                        //下学未在打卡时间段
                                        List<RecordDetailVO> endNotIn = recordDetailVOS.stream()
                                            .filter(recordDetailVO -> 20 == recordDetailVO.getStatus()).collect(Collectors.toList());

                                        //正常
                                        List<RecordDetailVO> nomal = recordDetailVOS.stream()
                                            .filter(recordDetailVO -> 0 == recordDetailVO.getStatus()).collect(Collectors.toList());

                                        if (late.size() > 0) {
                                            status = AttendanceResult.STU_LATE.getValue();
                                        }
                                        if (leave.size() > 0) {
                                            if (late.size() > 0) {
                                                status = AttendanceResult.STU_LATE_LEAVE.getValue();
                                            } else {
                                                status = AttendanceResult.STU_LEAVE.getValue();
                                            }
                                        }
                                        if (startNotIn.size() > 0) {
                                            status = AttendanceResult.STU_START_NOT_IN.getValue();
                                        }

                                        if (endNotIn.size() > 0) {
                                            status = AttendanceResult.STU_END_NOT_IN.getValue();
                                        }
                                        if (nomal.size() > 1) {
                                            //第一条记录
                                            RecordDetailVO firstRecord = nomal.get(0);
                                            //最后条记录
                                            RecordDetailVO lastRecord = nomal.get(nomal.size() - 1);

                                            if (firstRecord.getCreateTime().isBefore(time.getTime1().atDate(today)) && lastRecord
                                                .getCreateTime().isAfter(time.getTime2().atDate(today))) {
                                                status = AttendanceResult.NORMAL.getValue();
                                            }
                                        }
                                    }
                                }
                                result.setStatus(status);
                            }
                        } else { //住宿生
                            if (student.getDormRoomId() != null) {
                                if (todayDormRule != null) {
                                    time = todayDormTime;
                                    if (time == null) {
                                        continue;
                                    }
                                    if (LocalTime.now().isBefore(time.getTime3())) {
                                        continue;
                                    }

                                    //当前时段结束之前不统计旷宿
                                    if (ObjectUtil.isNotNull(time.getTime3())) {
                                        if (LocalTime.now().isBefore(time.getTime3())) {
                                            eduAttendanceResultMapper.delete(Wrappers.lambdaQuery(new EduAttendanceResult())
                                                .eq(EduAttendanceResult::getStudentId, student.getId())
                                                .eq(EduAttendanceResult::getRuleTimeId, time.getId())
                                                .eq(EduAttendanceResult::getCreateDate, LocalDate.now())
                                                .eq(EduAttendanceResult::getStatus, AttendanceResult.STU_D_NOTARRIVE.getValue()));
                                            continue;
                                        }
                                    } else {
                                        if (LocalTime.now().isBefore(time.getTime2())) {
                                            eduAttendanceResultMapper.delete(Wrappers.lambdaQuery(new EduAttendanceResult())
                                                .eq(EduAttendanceResult::getStudentId, student.getId())
                                                .eq(EduAttendanceResult::getRuleTimeId, time.getId())
                                                .eq(EduAttendanceResult::getCreateDate, LocalDate.now())
                                                .eq(EduAttendanceResult::getStatus, AttendanceResult.STU_D_NOTARRIVE.getValue()));
                                            continue;
                                        }
                                    }
                                    // 住校生
                                    //只统计旷宿就行了
                                    //考勤记录
                                    ListRecordDTO recordDTO = new ListRecordDTO();
                                    recordDTO.setStudentId(student.getId());
                                    recordDTO.setRuleTimeId(time.getId());
                                    recordDTO.setCreateDate(now.toLocalDate());
                                    recordDTO.setZone(1);
                                    List<RecordDetailVO> recordDetailVOS = eduAttendanceRecordMapper.listRecord(recordDTO);

                                    result.setRuleId(todayDormRule.getId());
                                    result.setRuleTimeId(time.getId());
                                    Integer status = null;
                                    if (CollUtil.isEmpty(recordDetailVOS)) {
                                        //无记录时添加结果
                                        List<EduAttendanceResult> eduAttendanceResults = eduAttendanceResultMapper
                                            .selectList(Wrappers.lambdaQuery(new EduAttendanceResult())
                                                .eq(EduAttendanceResult::getStudentId, student.getId())
                                                .eq(EduAttendanceResult::getRuleTimeId, time.getId())
                                                .eq(EduAttendanceResult::getCreateDate, LocalDate.now())
                                                .eq(EduAttendanceResult::getStatus, AttendanceResult.STU_D_NOTARRIVE.getValue()));

                                        if (CollUtil.isEmpty(eduAttendanceResults)) {
                                            status = AttendanceResult.STU_D_NOTARRIVE.getValue();
                                            result.setLastPushTime(LocalDateTime.now());
                                        }
                                    }
                                    result.setStatus(status);
                                    result.setAttendanceDate(today);

                                    // 是否核查
                                    if (time != null) {
                                        EduAttendanceVerifyDorm verifyDorm = eduAttendanceVerifyDormMapper
                                            .selectOne(Wrappers.<EduAttendanceVerifyDorm>lambdaQuery()
                                                .eq(EduAttendanceVerifyDorm::getDormRoomId, student.getDormRoomId())
                                                .eq(EduAttendanceVerifyDorm::getVerifyDate, attendanceDate)
                                                .eq(EduAttendanceVerifyDorm::getTime, time.getType()));
                                        if (verifyDorm != null) {
                                            result.setVerifyId(verifyDorm.getStaffId());
                                            result.setVerifyTime(verifyDorm.getCreateDate());
                                            result.setVerifyStatus(1);
                                        }
                                    }
                                }
                            }
                        }
                        if (result.getStatus() != null) {
                            saveResult.add(result);
                            updateNotArrivePush.add(result.getStudentId());
                        }
                    }
                }
            ));
        }
        for (Future future : futureList) {
            // 等待任务务完成
            try {
                future.get(100L, TimeUnit.SECONDS);
            } catch (Exception e) {
                future.cancel(false);
                log.error("等待生成考勤结果失败,schoolId:[{}], future:[{}]", config.getSchoolId(), future.isDone(), e);
            }
        }
        if (CollUtil.isNotEmpty(updateNotArrivePush)) {
            eduAttendanceResultService.update(Wrappers.<EduAttendanceResult>lambdaUpdate()
                .eq(EduAttendanceResult::getNotArriveConfirm, 0)
                .in(EduAttendanceResult::getStudentId, updateNotArrivePush)
                .in(EduAttendanceResult::getStatus, AttendanceResult.STU_NOTARRIVE.getValue(),
                    AttendanceResult.STU_D_NOTARRIVE.getValue())
                .set(EduAttendanceResult::getNotArriveConfirm, 1));
        }
        // 保存考勤记录
        if (CollUtil.isNotEmpty(saveResult)) {
            eduAttendanceResultService.saveBatch(saveResult);
        }
    }

    private RuleListVO matchRule(EduStudent student, List<RuleListVO> ruleList, Integer type) {
        if (CollUtil.isNotEmpty(ruleList)) {
            HashSet<Long> deptIds = CollUtil
                .newHashSet(student.getSchoolId(), student.getCampusId(), student.getSectionId(), student.getGradeId(),
                    student.getClassId());
            for (RuleListVO rule : ruleList) {
                if (type != null && !type.equals(rule.getType())) {
                    continue;
                }
                for (EduAttendanceRuleDept ruleDept : rule.getDeptList()) {
                    if (deptIds.contains(Math.abs(ruleDept.getDeptId()))) {
                        return rule;
                    }
                }
            }
        }
        return null;
    }
}
