package com.xjrsoft.module.attendance.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.enums.ArchivesStatusEnum;
import com.xjrsoft.common.model.result.RT;
import com.xjrsoft.common.page.ConventPage;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.module.attendance.dto.AttendanceStatisticDto;
import com.xjrsoft.module.attendance.dto.StudentDetailsDto;
import com.xjrsoft.module.attendance.vo.ClassStatisticsVo;
import com.xjrsoft.module.attendance.vo.StudentStatisticsPageVo;
import com.xjrsoft.module.base.entity.BaseClass;
import com.xjrsoft.module.base.service.IBaseClassService;
import com.xjrsoft.module.organization.entity.User;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.outint.entity.StudentOutInRecord;
import com.xjrsoft.module.outint.service.IStudentOutInRecordService;
import com.xjrsoft.module.outint.vo.StudentOutInRecordVo;
import com.xjrsoft.module.student.entity.BaseStudentFamily;
import com.xjrsoft.module.student.entity.BaseStudentSchoolRoll;
import com.xjrsoft.module.student.entity.StudentLeave;
import com.xjrsoft.module.student.service.IStudentLeaveService;
import com.xjrsoft.module.system.entity.DictionaryDetail;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @title: 考勤消息设置
* @Author dzx
* @Date: 2024-05-21
* @Version 1.0
*/
@RestController
@RequestMapping("/studentStatistics")
@Api(value = "/studentStatistics" ,tags = "学生考勤统计")
@AllArgsConstructor
public class StudentStatisticsController {


    private final IUserService xjrUserService;
    private final IStudentOutInRecordService studentOutInRecordService;
    private final IStudentLeaveService studentLeaveService;
    private final IBaseClassService classService;

    @GetMapping(value = "/class-statistics")
    @ApiOperation(value="班级考勤统计")
    @SaCheckPermission("statistics:detail")
    public RT<PageOutput<ClassStatisticsVo>> classStatistics(@Valid AttendanceStatisticDto dto){
        Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
        List<Long> classIds = new ArrayList<>();
        for (ClassStatisticsVo record : attendancePage.getRecords()) {
            classIds.add(record.getId());
        }
        if(dto.getDate() != null && !"".equals(dto.getDate())){
            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
            LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
            LocalDateTime startTime, endTime;


            if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
                startTime = queryDate.atTime(5, 0, 0);
                endTime = queryDate.atTime(12, 0, 0);
            }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
                startTime = queryDate.atTime(12, 0, 0);
                endTime = queryDate.atTime(18, 0, 0);
            }else{
                startTime = queryDate.atTime(0, 0, 0);
                endTime = queryDate.atTime(23, 59, 59);
            }
            LocalDateTime lastSundayStart = startTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);
            LocalDateTime lastSundayEnd = endTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);

            //查询每个班的走读生实到人数
            Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getList(startTime, endTime, classIds);

            //查询各班的请假人数
            Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);

            for (ClassStatisticsVo record: attendancePage.getRecords()) {
                Integer leaveCount = 0;
                if(classLeaveCount.get(record.getId()) != null){
                    leaveCount = classLeaveCount.get(record.getId());
                }
                record.setLeaveCount(leaveCount);
                int actualCount = 0;
                for (StudentOutInRecordVo outInRecordVo : notStayMap.get(record.getId())) {
                    if(outInRecordVo.getStatus() == 1){
                        actualCount ++;
                    }
                }
                record.setActualCount(actualCount);

                Integer lateCount = 0, playTruantCount = 0;
                for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
                    if(outInRecord.getStatus() == 0){
                        continue;
                    }
                    if("迟到".equals(outInRecord.getAttendanceStatus())){
                        lateCount ++;
                    }else if("旷课".equals(outInRecord.getAttendanceStatus())){
                        playTruantCount ++;
                    }
                }
                record.setPlayTruantCount(playTruantCount);
                record.setLateCount(lateCount);

                //最后通过总人数-实到人数-请假人数计算出缺勤人数
                record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());

                //计算出勤率
                BigDecimal divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
                record.setAttendanceRate(divide.doubleValue());
            }
        }
        PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
        return RT.ok(pageOutput);
    }


    @GetMapping(value = "/student-details")
    @ApiOperation(value="学生考勤")
    @SaCheckPermission("statistics:detail")
    public RT<PageOutput<StudentStatisticsPageVo>> studentDetails(@Valid StudentDetailsDto dto){
        MPJLambdaWrapper<User> queryUser = new MPJLambdaWrapper<>();
        queryUser.disableSubLogicDel().distinct()
                .like(StrUtil.isNotEmpty(dto.getName()), User::getName, dto.getName())
                .like(StrUtil.isNotEmpty(dto.getCredentialNumber()), User::getCredentialNumber, dto.getCredentialNumber())
                .eq(StrUtil.isNotEmpty(dto.getStduyStatus()), BaseStudentSchoolRoll::getStduyStatus, dto.getStduyStatus())
                .eq(ObjectUtil.isNotNull(dto.getClassId()), BaseStudentSchoolRoll::getClassId, dto.getClassId())
                .eq(BaseStudentSchoolRoll::getArchivesStatus, ArchivesStatusEnum.FB2901.getCode())
                .selectAs(BaseClass::getName, StudentStatisticsPageVo::getClassName)
                .select("t3.name", StudentStatisticsPageVo::getTeacherName)
                .selectAs(User::getName, StudentStatisticsPageVo::getStudentName)
                .selectAs(User::getMobile, StudentStatisticsPageVo::getMobile)
                .selectAs(User::getId, StudentStatisticsPageVo::getUserId)
                .selectAs(BaseStudentFamily::getTelephone, StudentStatisticsPageVo::getGuardianPhone)
                .selectAs(DictionaryDetail::getName, StudentStatisticsPageVo::getStduyStatusCn)
                .selectAs(User::getCredentialNumber, StudentStatisticsPageVo::getCredentialNumber)
                .innerJoin(BaseStudentSchoolRoll.class, BaseStudentSchoolRoll::getUserId, User::getId)
                .innerJoin(BaseClass.class, BaseClass::getId, BaseStudentSchoolRoll::getClassId)
                .leftJoin(User.class, User::getId, BaseClass::getTeacherId)
                .leftJoin(BaseStudentFamily.class, BaseStudentFamily::getUserId, User::getId)
                .leftJoin(DictionaryDetail.class, DictionaryDetail::getCode, BaseStudentSchoolRoll::getStduyStatus);
        IPage<StudentStatisticsPageVo> voIPage = xjrUserService.selectJoinListPage(ConventPage.getPage(dto), StudentStatisticsPageVo.class, queryUser);

        if(dto.getDate() != null && !"".equals(dto.getDate())){
            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
            LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
            LocalDateTime startTime, endTime;

            if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
                startTime = queryDate.atTime(5, 0, 0);
                endTime = queryDate.atTime(12, 0, 0);
            }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
                startTime = queryDate.atTime(12, 0, 0);
                endTime = queryDate.atTime(18, 0, 0);
            }else{
                startTime = queryDate.atTime(0, 0, 0);
                endTime = queryDate.atTime(23, 59, 59);
            }
            //查询当前时间段存在请假的学生
            Map<Long, StudentLeave> leaveList = studentLeaveService.getLeaveList(startTime, endTime);
            //查询进入记录
            List<StudentOutInRecord> outInRecords = studentOutInRecordService.list(
                    new QueryWrapper<StudentOutInRecord>().lambda()
                            .between(StudentOutInRecord::getRecordTime, startTime, endTime)
                            .eq(StudentOutInRecord::getStatus, 1)
            );
            Map<Long, StudentOutInRecord> outInMap = new HashMap<>();
            for (StudentOutInRecord inRecord : outInRecords) {
                outInMap.put(inRecord.getUserId(), inRecord);
            }
            for (StudentStatisticsPageVo record : voIPage.getRecords()) {
                StudentLeave studentLeave = leaveList.get(record.getUserId());
                if(studentLeave != null){
                    record.setStatus(studentLeave.getLeaveType());
                }else{
                    StudentOutInRecord outInRecord = outInMap.get(record.getUserId());
                    if(outInRecord != null){
                        record.setRecordTime(outInRecord.getRecordTime());
                        record.setStatus(outInRecord.getAttendanceStatus());
                    }else{
                        record.setStatus("缺勤");
                    }
                }
            }
        }
        PageOutput<StudentStatisticsPageVo> pageOutput = ConventPage.getPageOutput(voIPage, StudentStatisticsPageVo.class);
        return RT.ok(pageOutput);
    }


    @GetMapping(value = "/class-history")
    @ApiOperation(value="历史考勤")
    @SaCheckPermission("statistics:detail")
    public RT<PageOutput<ClassStatisticsVo>> classHistory(@Valid AttendanceStatisticDto dto){
        Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
        List<Long> classIds = new ArrayList<>();
        for (ClassStatisticsVo record : attendancePage.getRecords()) {
            classIds.add(record.getId());
        }
        if(dto.getStartTime() != null && !"".equals(dto.getStartTime()) && dto.getEndTime() != null && !"".equals(dto.getEndTime())){
            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
            LocalDateTime startTime = LocalDate.parse(dto.getStartTime(), formatter).atTime(0, 0, 0);
            LocalDateTime endTime = LocalDate.parse(dto.getEndTime(), formatter).atTime(23, 59, 59);

            //查询每个班的走读生实到人数
            Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getNotStayList(startTime, endTime, classIds);
            //查询住校生的实到情况
            Map<Long, List<StudentOutInRecordVo>> stayMap = studentOutInRecordService.getStayList(startTime, endTime, classIds);

            //查询各班的请假人数
            Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);

            //计算2个时间相差的天数
            long days = ChronoUnit.DAYS.between(startTime.toLocalDate(), endTime.toLocalDate());
            List<String> dayOfWeeks = new ArrayList<>();
            for (int i = 0; i <= days; i ++){
                dayOfWeeks.add(startTime.plusDays(i).getDayOfWeek().name());
            }

            for (ClassStatisticsVo record: attendancePage.getRecords()) {
                record.setLeaveCount(classLeaveCount.get(record.getId()) == null ? 0:classLeaveCount.get(record.getId()));
                record.setActualCount(notStayMap.get(record.getId()).size() + stayMap.get(record.getId()).size());
                record.setStudentCount(record.getStudentCount() * dayOfWeeks.size());

                Integer lateCount = 0;
                for (String dayOfWeek : dayOfWeeks) {
                    for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
                        if("迟到".equals(outInRecord.getAttendanceStatus())){
                            lateCount ++;
                        }
                    }
                    for (StudentOutInRecordVo outInRecord : stayMap.get(record.getId())) {
                        if("迟到".equals(outInRecord.getAttendanceStatus())){
                            lateCount ++;
                        }
                    }
                }

                record.setLateCount(lateCount);

                //最后通过总人数-实到人数-请假人数计算出缺勤人数
                record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());

                //计算出勤率
                BigDecimal divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
                record.setAttendanceRate(divide.doubleValue());
            }
        }
        PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
        return RT.ok(pageOutput);
    }

    @GetMapping(value = "/leaving-school")
    @ApiOperation(value="离校统计")
    @SaCheckPermission("statistics:detail")
    public RT<PageOutput<ClassStatisticsVo>> leavingSchool(@Valid AttendanceStatisticDto dto){
        Page<ClassStatisticsVo> attendancePage = classService.getAttendancePage(new Page<>(dto.getLimit(), dto.getSize()), dto);
        List<Long> classIds = new ArrayList<>();
        for (ClassStatisticsVo record : attendancePage.getRecords()) {
            classIds.add(record.getId());
        }
        if(dto.getDate() != null && !"".equals(dto.getDate())){
            DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE;
            LocalDate queryDate = LocalDate.parse(dto.getDate(), formatter);
            LocalDateTime startTime, endTime;


            if(dto.getTimePeriod() != null && dto.getTimePeriod() == 1){
                startTime = queryDate.atTime(5, 0, 0);
                endTime = queryDate.atTime(12, 0, 0);
            }else if(dto.getTimePeriod() != null && dto.getTimePeriod() == 2){
                startTime = queryDate.atTime(12, 0, 0);
                endTime = queryDate.atTime(18, 0, 0);
            }else{
                startTime = queryDate.atTime(0, 0, 0);
                endTime = queryDate.atTime(23, 59, 59);
            }
            LocalDateTime lastSundayStart = startTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);
            LocalDateTime lastSundayEnd = endTime.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).plusDays(-1);

            //查询每个班的走读生实到人数
            Map<Long, List<StudentOutInRecordVo>> notStayMap = studentOutInRecordService.getNotStayList(startTime, endTime, classIds);
            //查询住校生的实到情况
            Map<Long, List<StudentOutInRecordVo>> stayMap = studentOutInRecordService.getStayList(lastSundayStart, lastSundayEnd, classIds);

            //查询各班的请假人数
            Map<Long, Integer> classLeaveCount = studentLeaveService.getClassLeaveCount(startTime, endTime);

            for (ClassStatisticsVo record: attendancePage.getRecords()) {
                record.setLeaveCount(classLeaveCount.get(record.getId()));
                record.setActualCount(notStayMap.get(record.getId()).size() + stayMap.get(record.getId()).size());

                Integer lateCount = 0;
                for (StudentOutInRecordVo outInRecord : notStayMap.get(record.getId())) {
                    if(outInRecord.getStatus() == 1){
                        continue;
                    }
                    if("迟到".equals(outInRecord.getAttendanceStatus())){
                        lateCount ++;
                    }
                }
                for (StudentOutInRecordVo outInRecord : stayMap.get(record.getId())) {
                    if(outInRecord.getStatus() == 1){
                        continue;
                    }
                    if("迟到".equals(outInRecord.getAttendanceStatus())){
                        lateCount ++;
                    }
                }
                record.setLateCount(lateCount);

                //最后通过总人数-实到人数-请假人数计算出缺勤人数
                record.setAbsenteeismCount(record.getStudentCount() - record.getLeaveCount() - record.getActualCount());

                //计算出勤率
                BigDecimal divide = BigDecimal.valueOf(record.getActualCount()).divide(BigDecimal.valueOf(record.getStudentCount()), 4, RoundingMode.HALF_UP);
                record.setAttendanceRate(divide.doubleValue());
            }
        }
        PageOutput<ClassStatisticsVo> pageOutput = ConventPage.getPageOutput(attendancePage, ClassStatisticsVo.class);
        return RT.ok(pageOutput);
    }

}