package cn.iocoder.yudao.module.system.service.worklog;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.system.controller.admin.worklog.dto.statistics.LogStatisticsDetailsListDTO;
import cn.iocoder.yudao.module.system.controller.admin.worklog.dto.statistics.MyManageModelDTO;
import cn.iocoder.yudao.module.system.controller.admin.worklog.dto.statistics.NeedWriteHistoryDTO;
import cn.iocoder.yudao.module.system.controller.admin.worklog.vo.statistics.*;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.worklog.*;
import cn.iocoder.yudao.module.system.dal.mysql.worklog.LogFormMapper;
import cn.iocoder.yudao.module.system.dal.mysql.worklog.LogStatisticsMapper;
import cn.iocoder.yudao.module.system.dal.mysql.worklog.LogUseMapper;
import cn.iocoder.yudao.module.system.service.worklog.dto.LogUseVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作日志统计 Service 实现类
 *
 * @author 艾楷
 */
@Service
@Validated
public class LogStatisticsServiceImpl implements LogStatisticsService {
    @Resource
    private LogFormMapper logFormMapper;
    @Resource
    private LogInstanceService logInstanceService;

    @Resource
    private LogRuleService logRuleService;

    @Resource
    private LogUseMapper logUseMapper;

    @Resource
    private LogStatisticsMapper logStatisticsMapper;

    @Override
    public Long createLogStatistics(LogStatisticsSaveReqVO createReqVO) {
        // 插入
        LogStatisticsDO logStatistics = BeanUtils.toBean(createReqVO, LogStatisticsDO.class);
        logStatisticsMapper.insert(logStatistics);
        // 返回
        return logStatistics.getId();
    }

    @Override
    public void updateLogStatistics(LogStatisticsSaveReqVO updateReqVO) {
        // 更新
        LogStatisticsDO updateObj = BeanUtils.toBean(updateReqVO, LogStatisticsDO.class);
        logStatisticsMapper.updateById(updateObj);
    }

    @Override
    public void deleteLogStatistics(Long id) {
        // 删除
        logStatisticsMapper.deleteById(id);
    }


    @Override
    public LogStatisticsDO getLogStatistics(Long id) {
        return logStatisticsMapper.selectById(id);
    }

    @Override
    public PageResult<LogStatisticsDO> getLogStatisticsPage(LogStatisticsPageReqVO pageReqVO) {
        return logStatisticsMapper.selectPage(pageReqVO);
    }

    @Override
    public List<LogStatisticsModelVO> myManageModel() {
        List<LogStatisticsModelVO> list = new ArrayList<>();
        //获取模版ids - 当前登录用户的数据权限 查看日志模板使用者表
        List<Long> formIds = logUseMapper.getFormIdsByDataPermission(null);
        if (formIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<LogFormDO> formList = new ArrayList<>();
        Map<Long, LogRuleDO> ruleMap = new HashMap<>();
        //查询模版
        formList = logFormMapper.selectList(new LambdaQueryWrapper<LogFormDO>()
                .in(LogFormDO::getId, formIds)
                .eq(LogFormDO::getStatus, 0)
                .isNotNull(LogFormDO::getRuleId)
        );
        List<Long> ruleIds = formList.stream().map(LogFormDO::getRuleId).collect(Collectors.toList());
        List<LogRuleDO> logRuleDOS = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(ruleIds)) {
            logRuleDOS = logRuleService.getLogRuleByIds(ruleIds);
            ruleMap = logRuleDOS.stream().collect(Collectors.toMap(LogRuleDO::getId, v -> v));
        }


        Date thisDate = new Date();
        Date beginTime = null;
        Date endTime = null;
        for (LogFormDO logFormDO : formList) {
            //拿到模版规则
            LogRuleDO logRuleDO = ruleMap.get(logFormDO.getRuleId());
            LogStatisticsModelVO vo = new LogStatisticsModelVO();
            BeanUtil.copyProperties(logFormDO, vo);
            vo.setType(logRuleDO.getType());
            //查询下应该提交多少日志实例 - 直接通过模版id查询使用表
            Integer num = Math.toIntExact(logUseMapper.selectCount(new LambdaQueryWrapper<LogUseDO>()
                    .eq(LogUseDO::getFormId, logFormDO.getId())));
            boolean flag = false;
            if (logRuleDO.getType() == 1 && StrUtil.isNotEmpty(logRuleDO.getWeekDays())) {
                // -- 判断今天是周几 和 需要提交的周做对比
                int weekNum = DateUtils.dayOfWeek(thisDate);
                List<String> weekDays = Arrays.asList(logRuleDO.getWeekDays().split(","));
                if (weekDays.contains(Integer.toString(weekNum))) {
                    // -- 判断今天是周几 和 需要提交的周做对比
                    beginTime = DateUtil.beginOfDay(thisDate);
                    endTime = DateUtil.endOfDay(thisDate);
                    flag = true;
                    vo.setCommitTimeRange("当天" + logRuleDO.getEndTime() + "截止提交");
                } else {
                    vo.setCommitTimeRange("当天不需要提交");
                }
            } else if (logRuleDO.getType() == 2) {
                beginTime = DateUtil.beginOfWeek(thisDate);
                endTime = DateUtil.endOfWeek(thisDate);
                flag = true;
                vo.setCommitTimeRange("每周" + DateUtil.dayOfWeekEnum(endTime).toChinese() + (logRuleDO.getEndTime().split(",")[1]) + "截止提交");
            }
            if (flag) {
                List<String> dateList = DateUtils.betweenDayList(beginTime, endTime);
                List<LogInstanceDO> instanceDOS = logInstanceService.getNeedWriteLogInstanceByTimeRange(logFormDO.getId(), dateList, null);
                //按时提交数量
                Integer onTimeNum = Math.toIntExact(instanceDOS.stream().filter(a -> a.getStatus() == 1).count());
                //迟交数量
                Integer lateNum = Math.toIntExact(instanceDOS.stream().filter(a -> a.getStatus() == 2).count());
                //未提交数量
                Integer unSubmittedNum = Math.max((num - onTimeNum - lateNum), 0);
                //按天统计
                vo.setOnTimeNum(onTimeNum);
                vo.setUnSubmittedNum(unSubmittedNum);
                vo.setLateNum(lateNum);
            }
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<AdminUserDO> getUnSubmittedUser(Long formId, String thisTime) {
        return logStatisticsMapper.getUnSubmittedUser(formId, thisTime);
    }

    @Override
    public LogStatisticsDetailsListVO getStatistics(LogStatisticsDetailsListDTO dto) {
        LogStatisticsDetailsListVO vo = new LogStatisticsDetailsListVO();
        // 获取规则
        LogRuleDO logRuleDO = logRuleService.getLogRuleByFormId(dto.getFormId());
        // 判断开始时间是否当天 / 是否本周
        boolean flag = false;
        List<LogStatisticsDetailsVO> logStatisticsDOS = new ArrayList<>();
        List<String> dateList = new ArrayList<>();
        if (StrUtil.isNotEmpty(dto.getBeginTime()) && StrUtil.isNotEmpty(dto.getEndTime())) {
            Date begin = DateUtil.parse(dto.getBeginTime(), "yyyy-MM-dd").toJdkDate();
            if (logRuleDO.getType() == 1) {
                flag = DateUtil.isSameDay(begin, new Date());
            } else if (logRuleDO.getType() == 2) {
                flag = DateUtil.isSameWeek(begin, new Date(), true);
            }
            dateList = DateUtils.betweenDayStrList(dto.getBeginTime(), dto.getEndTime());
        }
        // 这里将区间转换为具体的日期 因为mysql BETWEEN 会导致索引失效
        if (flag) {
            logStatisticsDOS = logStatisticsMapper.getCurrentStatistics(dto.getFormId(), dateList);
        } else {
            logStatisticsDOS = logStatisticsMapper.getStatistics(dto.getFormId(), dto.getUserId(), dto.getDeptId(), dateList);
        }
        List<LogStatisticsDetailsVO> onTime = logStatisticsDOS.stream().filter(a -> a.getStatus() == 1).collect(Collectors.toList());
        List<LogStatisticsDetailsVO> late = logStatisticsDOS.stream().filter(a -> a.getStatus() == 2).collect(Collectors.toList());
        List<LogStatisticsDetailsVO> unSubmitted = logStatisticsDOS.stream().filter(a -> a.getStatus() == 3).collect(Collectors.toList());
        vo.setOnTime(onTime);
        vo.setLate(late);
        vo.setUnSubmitted(unSubmitted);
        return vo;
    }

    @Override
    public List<LogStatisticsModelVO> getNeedWrite() {
        List<LogStatisticsModelVO> list = new ArrayList<>();
        Long userId = WebFrameworkUtils.getLoginUserId();
        //获取模版ids - 当前登录用户的数据权限 查看日志模板使用者表
        List<Long> formIds = logUseMapper.getFormIdsByDataPermission(userId);
        if (formIds.isEmpty()) {
            return Collections.emptyList();
        }
        //获取模版和规则
        List<LogFormDO> formList = new ArrayList<>();
        Map<Long, LogRuleDO> ruleMap = new HashMap<>();
        //查询模版
        formList = logFormMapper.selectList(new LambdaQueryWrapper<LogFormDO>()
                .in(LogFormDO::getId, formIds)
                .eq(LogFormDO::getStatus, 0)
                .isNotNull(LogFormDO::getRuleId)
        );
        List<Long> ruleIds = formList.stream().map(LogFormDO::getRuleId).collect(Collectors.toList());
        List<LogRuleDO> logRuleDOS = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(ruleIds)) {
            logRuleDOS = logRuleService.getLogRuleByIds(ruleIds);
            ruleMap = logRuleDOS.stream().collect(Collectors.toMap(LogRuleDO::getId, v -> v));
        }

        Date thisDate = new Date();
        Date beginDate = null;
        Date endDate = null;
        for (LogFormDO logFormDO : formList) {
            //拿到模版规则
            LogRuleDO logRuleDO = ruleMap.get(logFormDO.getRuleId());
            LogStatisticsModelVO vo = new LogStatisticsModelVO();
            BeanUtil.copyProperties(logFormDO, vo);
            //判断当天/本周是否需要提交
            boolean flag = true;
            if (logRuleDO.getType() == 1 && StrUtil.isNotEmpty(logRuleDO.getWeekDays())) {
                // -- 判断今天是周几 和 需要提交的周做对比
                int weekNum = DateUtils.dayOfWeek(thisDate);
                List<String> weekDays = Arrays.asList(logRuleDO.getWeekDays().split(","));
                if (weekDays.contains(Integer.toString(weekNum))) {
                    vo.setCommitTimeRange("当天" + logRuleDO.getEndTime() + "截止提交");
                    beginDate = DateUtil.beginOfDay(thisDate);
                    endDate = DateUtil.endOfDay(thisDate);
                } else {
                    flag = false;
                    vo.setCommitTimeRange("当天不需要提交");
                }
            } else if (logRuleDO.getType() == 2) {
                beginDate = DateUtil.beginOfWeek(thisDate);
                endDate = DateUtil.endOfWeek(thisDate);
                // -- 拼接获取到结束时间
                Date endTime = DateUtils.buildWeekHHmmTime(logRuleDO.getEndTime());
                vo.setCommitTimeRange("每周" + DateUtil.dayOfWeekEnum(endTime).toChinese() + (logRuleDO.getEndTime().split(",")[1]) + "截止提交");
            }
            // -- 需要提交的话查看下是否需要提交
            if (flag) {
                List<String> dateList = DateUtils.betweenDayList(beginDate, endDate);
                List<LogUseVO> logUseVOS = logStatisticsMapper.getMyCurrentStatistics(userId, null, logFormDO.getId(), dateList);
                //按时提交数量
                Integer onTimeNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 1).count());
                //迟交数量
                Integer lateNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 2).count());
                //未提交数量
                Integer unSubmittedNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 3).count());
                //按天统计
                vo.setOnTimeNum(onTimeNum);
                vo.setUnSubmittedNum(unSubmittedNum);
                vo.setLateNum(lateNum);
            }
            vo.setType(logRuleDO.getType());
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<LogStatisticsDetailsVO> exportLogRuleExcel(LogStatisticsDetailsListDTO dto) {
        // 获取规则
        LogRuleDO logRuleDO = logRuleService.getLogRuleByFormId(dto.getFormId());
        // 判断开始时间是否当天 / 是否本周
        boolean flag = false;
        Date begin = DateUtil.parse(dto.getBeginTime(), "yyyy-MM-dd").toJdkDate();
        List<LogStatisticsDetailsVO> logStatisticsDOS = new ArrayList<>();
        Date thisDate = new Date();
        if (logRuleDO.getType() == 1) {
            flag = DateUtil.isSameDay(begin, thisDate);
        } else if (logRuleDO.getType() == 2) {
            flag = DateUtil.isSameWeek(begin, thisDate, true);
        }
        List<String> dateList = DateUtils.betweenDayStrList(dto.getBeginTime(), dto.getEndTime());
        // 这里将区间转换为具体的日期 因为mysql BETWEEN 会导致索引失效
        if (flag) {
            logStatisticsDOS = logStatisticsMapper.getCurrentStatistics(dto.getFormId(), dateList);
        } else {
            logStatisticsDOS = logStatisticsMapper.getStatistics(dto.getFormId(), dto.getUserId(), dto.getDeptId(), dateList);
        }
        return logStatisticsDOS;
    }

    @Override
    public void saveOrUpdate(LogStatisticsDO logStatisticsDO) {
        if (logStatisticsDO.getStatus() == 1) {
            logStatisticsMapper.insert(logStatisticsDO);
            return;
        }
        List<LogStatisticsDO> logStatisticsDOS = logStatisticsMapper.selectList(new LambdaQueryWrapper<LogStatisticsDO>()
                .eq(LogStatisticsDO::getUserId, logStatisticsDO.getUserId())
                .eq(LogStatisticsDO::getFormId, logStatisticsDO.getFormId())
                .eq(LogStatisticsDO::getTime, logStatisticsDO.getTime()));
        if (CollUtil.isEmpty(logStatisticsDOS)) {
            logStatisticsMapper.insert(logStatisticsDO);
        } else {
            logStatisticsMapper.update(logStatisticsDO, new LambdaUpdateWrapper<LogStatisticsDO>()
                    .eq(LogStatisticsDO::getUserId, logStatisticsDO.getUserId())
                    .eq(LogStatisticsDO::getFormId, logStatisticsDO.getFormId())
                    .eq(LogStatisticsDO::getTime, logStatisticsDO.getTime()));
        }
    }

    @Override
    public String getCommitTimeRange(LogRuleDO logRuleDO) {
        Date thisDate = new Date();
        if (logRuleDO.getType() == 1 && StrUtil.isNotEmpty(logRuleDO.getWeekDays())) {
            // -- 判断今天是周几 和 需要提交的周做对比
            int weekNum = DateUtils.dayOfWeek(thisDate);
            List<String> weekDays = Arrays.asList(logRuleDO.getWeekDays().split(","));
            if (weekDays.contains(Integer.toString(weekNum))) {
                return "当天" + logRuleDO.getEndTime() + "截止提交";
            } else {
                return "当天不需要提交";
            }
        } else if (logRuleDO.getType() == 2) {
            // -- 拼接获取到结束时间
            Date endTime = DateUtils.buildWeekHHmmTime(logRuleDO.getEndTime());
            return "每周" + DateUtil.dayOfWeekEnum(endTime).toChinese() + (logRuleDO.getEndTime().split(",")[1]) + "截止提交";
        }
        return null;
    }

    @Override
    public List<LogStatisticsModelVO> getNeedWriteHistory(NeedWriteHistoryDTO dto) {
        List<String> dateList = new ArrayList<>();
        if (dto.getBeginTime() != null && dto.getEndTime() != null) {
            dateList = DateUtils.betweenDayList(dto.getBeginTime(), dto.getEndTime());
        }
        dto.setUserId(SecurityFrameworkUtils.getLoginUserId());
        // 这里将区间转换为具体的日期 因为mysql BETWEEN 会导致索引失效
        return logStatisticsMapper.getNeedWriteHistory(dto, dateList);
    }

    @Override
    public List<LogStatisticsModelVO> getMyManageModelStatistics(MyManageModelDTO dto) {
        List<Long> formIds = logUseMapper.getFormIdsByDataPermission(null);
        if (formIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<LogStatisticsModelVO> list = new ArrayList<>();
        List<LogFormDO> formList = logFormMapper.selectList(new LambdaQueryWrapper<LogFormDO>()
                .in(LogFormDO::getId, formIds)
                .eq(LogFormDO::getStatus, 0)
                .isNotNull(LogFormDO::getRuleId)
        );

        for (LogFormDO logFormDO : formList) {
            LogStatisticsModelVO vo = new LogStatisticsModelVO();
            BeanUtil.copyProperties(logFormDO, vo);
            List<String> dateList = new ArrayList<>();
            if (dto.getBeginTime() != null && dto.getEndTime() != null) {
                dateList = DateUtils.betweenDayList(dto.getBeginTime(), dto.getEndTime());
            }
            List<LogUseVO> logUseVOS = logStatisticsMapper.getMyCurrentStatistics(dto.getUserId(), dto.getDeptId(), logFormDO.getId(), dateList);
            //按时提交数量
            Integer onTimeNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 1).count());
            //迟交数量
            Integer lateNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 2).count());
            //未提交数量
            Integer unSubmittedNum = Math.toIntExact(logUseVOS.stream().filter(a -> a.getStatus() == 3).count());
            //按天统计
            vo.setOnTimeNum(onTimeNum);
            vo.setUnSubmittedNum(unSubmittedNum);
            vo.setLateNum(lateNum);
            list.add(vo);
        }
        return list;
    }
}