package cn.iocoder.yudao.module.smartfactory.service.attendance;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.smartfactory.controller.admin.attendance.vo.AttendanceRecordVO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.attendance.vo.StaffAttendanceRecordReqVO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.attendance.vo.StaffAttendanceRecordRespVO;
import cn.iocoder.yudao.module.smartfactory.controller.admin.attendance.vo.StaffAttendanceRecordSaveReqVO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.attendance.SfAttendanceReviewDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.attendance.StaffAttendanceRecordDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.factoryinfo.FactoryInfoDO;
import cn.iocoder.yudao.module.smartfactory.dal.dataobject.staff.StaffDO;
import cn.iocoder.yudao.module.smartfactory.dal.mysql.attendance.SfAttendanceReviewMapper;
import cn.iocoder.yudao.module.smartfactory.dal.mysql.attendance.StaffAttendanceRecordMapper;
import cn.iocoder.yudao.module.smartfactory.service.factoryinfo.FactoryInfoService;
import cn.iocoder.yudao.module.smartfactory.service.staff.StaffService;
import cn.iocoder.yudao.module.smartfactory.service.staffsalary.StaffSalaryService;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.smartfactory.enums.ErrorCodeConstants.STAFF_ATTENDANCE_RECORD_NOT_EXISTS;

/**
 * 厂区员工考勤记录 Service 实现类
 *
 * @author 符溶馨
 */
@Service
@Validated
public class StaffAttendanceRecordServiceImpl implements StaffAttendanceRecordService {

    @Resource
    private StaffAttendanceRecordMapper staffAttendanceRecordMapper;

    @Resource
    private StaffSalaryService staffSalaryService;

    @Resource
    private SfAttendanceReviewMapper attendanceReviewMapper;

    @Resource
    private StaffService staffService;

    @Resource
    private FactoryInfoService factoryInfoService;

    @Override
    public void createStaffAttendanceRecord(List<StaffAttendanceRecordSaveReqVO> createReqVO) {

        if (CollUtil.isEmpty(createReqVO)) {
            return;
        }
        // 新增前 全量清楚考勤记录
        staffAttendanceRecordMapper.deleteRecord(createReqVO.get(0));

        List<StaffAttendanceRecordDO> createDo = new ArrayList<>();
        for (StaffAttendanceRecordSaveReqVO vo : createReqVO) {

            List<StaffAttendanceRecordDO> staffAttendanceRecord = BeanUtils.toBean(vo.getRecords(), StaffAttendanceRecordDO.class);
            // 移除status为空得数据
            staffAttendanceRecord.removeIf(item -> item.getStatus() == null);

            staffAttendanceRecord.forEach(item -> {
                // 设置厂区
                item.setFactoryId(vo.getFactoryId());
                // 设置用户
                item.setStaffId(vo.getStaffId());
                // 设置打卡类型
                item.setType(vo.getType());
                // 设置考勤月份
                item.setMonth(vo.getMonth());
            });

            createDo.addAll(staffAttendanceRecord);
        }
        // 插入
        staffAttendanceRecordMapper.insertBatch(createDo);
    }

    @Override
    public void updateStaffAttendanceRecord(List<StaffAttendanceRecordSaveReqVO> updateReqVO) {

        // 更新
        List<StaffAttendanceRecordDO> updateObj = updateReqVO.stream()
                .flatMap(item -> BeanUtils.toBean(item.getRecords(), StaffAttendanceRecordDO.class).stream())
                .collect(Collectors.toList());

        staffAttendanceRecordMapper.updateBatch(updateObj);
    }

    @Override
    public void deleteStaffAttendanceRecord(Long id) {
        // 校验存在
        validateStaffAttendanceRecordExists(id);
        // 删除
        staffAttendanceRecordMapper.deleteById(id);
    }

    @Override
    public List<StaffAttendanceRecordDO> getRecord(Long staffId, String month, Integer type) {

        List<StaffAttendanceRecordDO> recordDOS = new ArrayList<>();
        // 如果考勤方式 是上月25至本月24号 时
        if (type == 1) {
            // 获取当前月份 上一个月得第一天
            LocalDate date = LocalDate.parse(month + "-01", DateTimeFormatter.ISO_LOCAL_DATE)
                    .minusMonths(1);
            // 获取上月25日日期
            LocalDate beginDate = date.withDayOfMonth(25);
            // 计算上个月的最后一天
            LocalDate lastMonthEnd = date.withDayOfMonth(date.lengthOfMonth()); // 设置为上个月的最后一天

            List<StaffAttendanceRecordDO> lastMonthRecord = staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                    .eq(StaffAttendanceRecordDO::getStaffId, staffId)
                    .betweenIfPresent(StaffAttendanceRecordDO::getPunchTime, new LocalDate[]{ beginDate, lastMonthEnd }));

            recordDOS.addAll(lastMonthRecord);
        }

        // 获取当前月份得考勤记录
        List<StaffAttendanceRecordDO> currentMonthRecord = staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                .eq(StaffAttendanceRecordDO::getStaffId, staffId)
                .like(StaffAttendanceRecordDO::getPunchTime, month));
        recordDOS.addAll(currentMonthRecord);

        return recordDOS;
    }

    private void validateStaffAttendanceRecordExists(Long id) {
        if (staffAttendanceRecordMapper.selectById(id) == null) {
            throw exception(STAFF_ATTENDANCE_RECORD_NOT_EXISTS);
        }
    }

    @Override
    public Integer getType(Long factoryId, String month) {
        List<StaffAttendanceRecordDO> recordDOS = staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                .eq(StaffAttendanceRecordDO::getFactoryId, factoryId)
                .eq(StaffAttendanceRecordDO::getMonth, month));

        if (CollUtil.isNotEmpty(recordDOS)) {
            return recordDOS.get(0).getType();
        }
        return null;
    }

    @Override
    public List<StaffAttendanceRecordDO> getStaffAttendanceRecordList(StaffAttendanceRecordReqVO reqVO) {

        return staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                .eqIfPresent(StaffAttendanceRecordDO::getStaffId, reqVO.getStaffId())
                .eqIfPresent(StaffAttendanceRecordDO::getFactoryId, reqVO.getFactoryId())
                .eqIfPresent(StaffAttendanceRecordDO::getMonth, reqVO.getMonth()));
    }

    @Override
    public List<StaffAttendanceRecordDO> getOtherRecord(StaffAttendanceRecordReqVO reqVO) {
        LocalDate[] date = null;
        if (reqVO.getType() == 1 && reqVO.getMonth() != null) {
            // 获取查询月份第一天
            LocalDate now = LocalDate.parse(reqVO.getMonth() + "-01", DateTimeFormatter.ISO_LOCAL_DATE);
            // 获取上月26日日期
            LocalDate beginDate = now.minusMonths(1).withDayOfMonth(26);
            // 获取查询月份得第25日日期
            LocalDate lastMonthEnd = now.withDayOfMonth(25);

            date = new LocalDate[]{ beginDate, lastMonthEnd };
        }
        return staffAttendanceRecordMapper.selectRecordList(reqVO, date);
    }

    @Override
    public List<StaffAttendanceRecordDO> getRecordListGroupByStaffId(String month) {

        return staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                .eq(StaffAttendanceRecordDO::getStatus, 1)
                .eq(StaffAttendanceRecordDO::getMonth, month));
    }

    @Override
    public void updateReview(Long factoryId, String month) {

        attendanceReviewMapper.insert(new SfAttendanceReviewDO()
                .setFactoryId(factoryId)
                .setMonth(month)
                .setStatus(1));

        List<StaffAttendanceRecordDO> createDo = staffAttendanceRecordMapper.selectList(new LambdaQueryWrapperX<StaffAttendanceRecordDO>()
                .eq(StaffAttendanceRecordDO::getFactoryId, factoryId)
                .eq(StaffAttendanceRecordDO::getMonth, month));
        // 审核考勤记录后， 自动计算员工薪资
        staffSalaryService.calculateSalary(createDo);
    }

    @Override
    public Integer getIsReview(Long factoryId, String month) {
        SfAttendanceReviewDO attendanceReviewDO = attendanceReviewMapper.selectOne(new LambdaQueryWrapperX<SfAttendanceReviewDO>()
                .eq(SfAttendanceReviewDO::getFactoryId, factoryId)
                .eq(SfAttendanceReviewDO::getMonth, month));
        return attendanceReviewDO != null ? attendanceReviewDO.getStatus() : 0;
    }

    @Override
    public List<StaffAttendanceRecordRespVO> convertRecord(StaffAttendanceRecordReqVO reqVO,
                                                           List<StaffAttendanceRecordDO> recordDOS,
                                                           List<StaffAttendanceRecordDO> otherRecordDOS) {
        // 根据考勤类型 生成对应得日期数据
        List<AttendanceRecordVO> punchTimeList = new ArrayList<>();

        // 获取所选月份的第一天
        LocalDate now = LocalDate.parse(reqVO.getMonth() + "-01", DateTimeFormatter.ISO_LOCAL_DATE);
        if (reqVO.getType() == 1) {

            // 获取上月26日日期
            LocalDate beginDate = now.minusMonths(1).withDayOfMonth(26);
            // 本月25日日期
            LocalDate lastMonthEnd = now.withDayOfMonth(25);

            while (!beginDate.isAfter(lastMonthEnd)) {
                AttendanceRecordVO attendanceRecordVO = new AttendanceRecordVO()
                        .setPunchTime(beginDate);
                punchTimeList.add(attendanceRecordVO);

                beginDate = beginDate.plusDays(1);
            }
        }
        if (reqVO.getType() == 2) {
            // 获取所选月份的第一天
            LocalDate firstDayOfMonth = LocalDate.parse(reqVO.getMonth() + "-01", DateTimeFormatter.ISO_LOCAL_DATE);

            // 生成当月每一天的 LocalDate
            for (int i = 0; i < firstDayOfMonth.lengthOfMonth(); i++) {
                AttendanceRecordVO attendanceRecordVO = new AttendanceRecordVO()
                        .setPunchTime(firstDayOfMonth.plusDays(i));
                punchTimeList.add(attendanceRecordVO);
            }
        }
        // 获取员工信息
        List<StaffDO> staffDOS = staffService.getList(convertSet(recordDOS, StaffAttendanceRecordDO::getStaffId));
        Map<Long, StaffDO> staffMap = convertMap(staffDOS, StaffDO::getId);

        // 获取厂区信息
        Set<Long> factoryIds = convertSet(recordDOS, StaffAttendanceRecordDO::getFactoryId);
        factoryIds.addAll(convertList(otherRecordDOS, StaffAttendanceRecordDO::getFactoryId));
        List<FactoryInfoDO> factoryDOS = factoryInfoService.getFactoryList(factoryIds);
        Map<Long, FactoryInfoDO> factoryMap = convertMap(factoryDOS, FactoryInfoDO::getId);

        // 根据用户 分组打卡记录
        Map<Long, List<StaffAttendanceRecordDO>> recordMap = convertMultiMap(recordDOS, StaffAttendanceRecordDO::getStaffId);

        // 重组数据
        List<StaffAttendanceRecordRespVO> respVOs = new ArrayList<>();
        for (Map.Entry<Long, List<StaffAttendanceRecordDO>> entry : recordMap.entrySet()) {

            if (!staffMap.containsKey(entry.getKey())) {
                continue;
            }

            // 设置所选厂区 员工得考勤信息
            StaffAttendanceRecordRespVO respVO = new StaffAttendanceRecordRespVO(
                    entry.getKey(),
                    reqVO.getFactoryId(),
                    reqVO.getType(),
                    reqVO.getMonth());

            Map<LocalDate, AttendanceRecordVO> records = entry.getValue().stream()
                    .filter(record -> record.getFactoryId().equals(reqVO.getFactoryId())) // 筛选同一个工厂打卡数据
                    .map(item -> BeanUtils.toBean(item, AttendanceRecordVO.class))
                    .collect(Collectors.toMap(AttendanceRecordVO::getPunchTime, value -> value));
            // 设置打卡数据
            List<AttendanceRecordVO> recordVOS = punchTimeList.stream()
                    .map(record -> {
                        AttendanceRecordVO recordVO = BeanUtils.toBean(record, AttendanceRecordVO.class);
                        if (records.containsKey(record.getPunchTime())) {
                            recordVO.setStatus(records.get(record.getPunchTime()).getStatus());
                        }
                        return recordVO;
                    }).collect(Collectors.toList());
            respVO.setRecords(recordVOS);
            // 设置员工名称
            respVO.setStaffName(staffMap.get(entry.getKey()).getNickName());
            // 设置工种
            respVO.setWorkTypeId(staffMap.get(entry.getKey()).getWorkTypeId());
            // 设置厂区名称
            respVO.setFactoryName(factoryMap.get(reqVO.getFactoryId()) != null ? factoryMap.get(reqVO.getFactoryId()).getName() : null);

            if (CollUtil.isNotEmpty(otherRecordDOS)) {
                // 获得该员工 在其他工厂的考勤记录
                Map<Long, List<StaffAttendanceRecordDO>> factoryRecordMap = convertMultiMap(otherRecordDOS, StaffAttendanceRecordDO::getFactoryId);

                List<StaffAttendanceRecordRespVO> childRespVO = otherRecordDOS.stream()
                        .map(recordDO -> new StaffAttendanceRecordRespVO(
                                recordDO.getStaffId(),
                                recordDO.getFactoryId(),
                                recordDO.getType(),
                                recordDO.getMonth()))
                        .distinct()
                        .collect(Collectors.toList());

                childRespVO.forEach(vo -> {
                    if (!staffMap.containsKey(vo.getStaffId())) {
                        return;
                    }
                    // 设置员工名称
                    vo.setStaffName(staffMap.get(vo.getStaffId()).getNickName());
                    // 设置工种
                    vo.setWorkTypeId(staffMap.get(vo.getStaffId()).getWorkTypeId());
                    // 设置厂区名称
                    vo.setFactoryName(factoryMap.get(vo.getFactoryId()) != null ? factoryMap.get(vo.getFactoryId()).getName() : null);

                    if (factoryRecordMap.containsKey(vo.getFactoryId())) {

                        // 设置打卡记录
                        vo.setRecords(BeanUtils.toBean(factoryRecordMap.get(vo.getFactoryId()), AttendanceRecordVO.class));
                    }
                });
                // 插入其他考勤记录
                respVO.setChildRecords(childRespVO);
            }

            // 插入集合
            respVOs.add(respVO);
        }

        return respVOs;
    }
}