package com.ruoyi.ibs.task.service.impl;

import com.github.pagehelper.Page;
import com.ruoyi.common.core.page.TableDataPageInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.ibs.dashboard.domain.vo.WarnInfoVO;
import com.ruoyi.ibs.task.domain.dto.WorkRecordDTO;
import com.ruoyi.ibs.task.domain.entity.Alter;
import com.ruoyi.ibs.task.domain.entity.WorkRecord;
import com.ruoyi.ibs.task.domain.vo.AlterVO;
import com.ruoyi.ibs.task.domain.vo.WorkCompleteVO;
import com.ruoyi.ibs.task.domain.vo.WorkRecordVO;
import com.ruoyi.ibs.task.mapper.WorkRecordMapper;
import com.ruoyi.ibs.task.service.WorkRecordService;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.ISysPostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 工作清单记录Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-10-11
 */
@Slf4j
@Service
public class WorkRecordServiceImpl implements WorkRecordService {

    @Autowired
    private WorkRecordMapper workRecordMapper;

    @Autowired
    private ISysPostService sysPostService;

    @Resource
    private RedisCache redisCache;

    private final static String alterTypeRedisKey = "work:alter_types_count_";

    /**
     * 查询我的工作清单
     * 
     * @param workRecordDTO 工作清单记录
     * @return 工作清单记录
     */
    @Override
    public List<WorkRecordVO> getWorkRecordList(WorkRecordDTO workRecordDTO) {
        String username = SecurityUtils.getUsername();
        List<WorkRecordVO> workRecordVOs = workRecordMapper.selectWorkRecordList(workRecordDTO, username);
        return workRecordVOs;
    }

    /**
     * 修改工作清单记录（只能修改状态和备注）
     * 
     * @param workRecordList 工作清单记录
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchSubmitWorkRecord(List<WorkRecord> workRecordList) {
        if (workRecordList == null || workRecordList.isEmpty()){
            return 0;
        }
        workRecordList.forEach(record -> record.setUpdateBy(SecurityUtils.getUsername()));
        return workRecordMapper.batchSubmitWorkRecord(workRecordList);
    }

    /**
     * 查询权限内用户工作清单完成情况（只有管理员有该功能）
     *
     * 1、查询当前登录用户权限范围内所有post_ids
     * 2、根据post_ids查询work_record表的所有集合，按照用户分组
     * 3、评估每个用户完成情况
     *
     * @param workRecordDTO 工作清单记录主键
     * @return 工作清单记录
     */
    @Override
    public TableDataPageInfo<WorkCompleteVO> getCompleteStatus(WorkRecordDTO workRecordDTO) {
        List<SysPost> sysPosts = sysPostService.selectPostList(new SysPost());
        if (sysPosts == null || sysPosts.isEmpty()){
            return PageUtils.getDataTable(new ArrayList<>(), new Page<>());
        }
        List<Long> postIds = sysPosts.stream()
                .map(SysPost::getPostId)
                .collect(Collectors.toList());
        //开启分页 避开上一个sql
        Page<Object> page = PageUtils.startPage();
        if (postIds.isEmpty()){
            return PageUtils.getDataTable(new ArrayList<>(), page);
        }
        List<WorkCompleteVO> voList= workRecordMapper.selectCompleteStatus(workRecordDTO, postIds);
        //计算排名
        int rankNum = (page.getPageNum() - 1) * page.getPageSize() + 1;
        if (voList != null && !voList.isEmpty()){
            IntStream.range(0, voList.size()).forEach(i -> voList.get(i).setRankNum(rankNum + i));
        }
        return PageUtils.getDataTable(voList, page);
    }

    /**
     * 获取我的预警信息
     *
     * @param alterType 预警类型
     * @return 预警信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AlterVO> getAlterList(String status, String alterType) {
        String userRole = SecurityUtils.userRole();
        String username = SecurityUtils.getUsername();
        if(!userRole.equals("manager") && alterType.equals("贷款流失提醒")){
            //如果角色是管理员，那么贷款流失提醒修改为不需要反馈的模式
            workRecordMapper.updateAlterByRole(alterType, username);
        }
        return workRecordMapper.selectAlterList(username, status, alterType);
    }

    /**
     * 修改预警信息
     *
     * @param alter 预警信息
     * @return 结果
     */
    @Override
    public int updateAlter(Alter alter) {
        alter.setUpdateBy(SecurityUtils.getUsername());
        alter.setUpdateTime(new Date());
        return workRecordMapper.updateAlter(alter);
    }

    @Override
    public int updateReadTime(Long id) {
        return workRecordMapper.updateReadTimeById(id, SecurityUtils.getUsername());
    }

    /**
     * 定时任务生成工作清单
     *
     * 1、根据频率类型计算时间范围
     * 2、查询模板表里当前频率类型处于启用状态下的工作清单（联表查用户信息等）
     * 3、插入工作清单记录表
     *
     * @param frequencyType 频率类型
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int generateWorkRecordScheduled(String frequencyType) {
        log.info("=====开始执行生成工作清单定时任务=====,频率类型为: " + frequencyType);
        //计算时间范围
        LocalDate today = LocalDate.now();
        Date startTime = calculateStartTime(frequencyType, today);
        Date endTime = calculateEndTime(frequencyType, today);
        log.info("时间范围: {} 至 {}", startTime, endTime);
        List<WorkRecord> recordsToGenerate = workRecordMapper.selectRecordsToGenerate(frequencyType, startTime, endTime);
        if (recordsToGenerate.isEmpty()){
            log.info("没有需要生成的记录");
            return 0;
        }
        log.info("需要生成的记录数量: {}", recordsToGenerate.size());
        int insertCount = workRecordMapper.batchInsertWorkRecord(recordsToGenerate);
        log.info("=====生成工作清单定时任务执行成功=====, 生成工作清单记录数量: {}", insertCount);
        return insertCount;
    }

    /**
     * 定时判断工作清单是否逾期，每日凌晨一点判断一次
     *
     * 1、获取当前时间
     * 2、查询表里当前时间在start_time和end_time之间，并且is_overdue为0的记录，修改is_overdue为1
     *
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int setOverdue(){
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        log.info("=====开始执行设置逾期工作清单定时任务=====,当前时间为: " + now);
        //查询工作清单记录
        return workRecordMapper.updateOverdueRecord(now);
    }

    /**
     * 获取预警类型及各类型预警数量
     * 每天首次查询存储到redis里，后面都从redis读取
     */
    @Override
    public List<WarnInfoVO> getAlterTypes() {
        String headId = SecurityUtils.getHeadId();
        String redisKey = alterTypeRedisKey + headId;

        // 先从 redis 获取缓存
        if (redisCache.hasKey(redisKey)) {
            List<WarnInfoVO> cachedList = redisCache.getCacheObject(redisKey);
            if (cachedList != null && !cachedList.isEmpty()) {
                log.debug("从 redis 获取预警类型缓存, headId: {}", headId);
                return cachedList;
            }
        }

        String username = SecurityUtils.getUsername();

        // 先查询预警类型列表
        List<String> alterTypes = workRecordMapper.selectAlterTypesByHeadId(headId);

        // 为每个预警类型查询对应的预警数量
        List<WarnInfoVO> resultList = alterTypes.stream().map(alterType -> {
            // 调用 getAlterList 方法获取该类型的所有预警记录（status为空表示查询全部）
            List<AlterVO> alterList = getAlterList("", alterType);
            WarnInfoVO warnInfoVO = new WarnInfoVO();
            warnInfoVO.setInfoName(alterType);
            warnInfoVO.setInfoCount((long) alterList.size());
            return warnInfoVO;
        }).collect(Collectors.toList());

        // 存入 redis，过期时间 1 天
        redisCache.setCacheObject(redisKey, resultList, 86400, TimeUnit.SECONDS);
        log.debug("预警类型数据已存入 redis, headId: {}, 数量: {}", headId, resultList.size());

        return resultList;
    }

    /**
     * 计算工作清单开始时间
     * @param frequencyType
     * @param date
     * @return
     */
    private Date calculateStartTime(String frequencyType, LocalDate date){
        LocalDateTime startTime;
        switch (frequencyType){
            case "0":
                startTime = LocalDateTime.of(date, LocalTime.MIN);
                break;
            case "1":
                startTime= LocalDateTime.of(date.with(DayOfWeek.MONDAY), LocalTime.MIN);
                break;
            case "2":
                startTime = LocalDateTime.of(date.with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
                break;
            case "3":
                int quarter = (date.getMonthValue() - 1) / 3;
                int firstMonthOfQuarter = quarter * 3 + 1;
                startTime = LocalDateTime.of(LocalDate.of(date.getYear(), firstMonthOfQuarter,1), LocalTime.MIN);
                break;
            case "4":
                startTime = LocalDateTime.of(LocalDate.of(date.getYear(), 1, 1), LocalTime.MIN);
                break;
            default:
                throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
        }
        return java.sql.Timestamp.valueOf(startTime);
    }

    /**
     * 计算工作清单结束时间
     * @param frequencyType
     * @param date
     * @return
     */
    private Date calculateEndTime(String frequencyType, LocalDate date){
        LocalDateTime endTime;
        switch (frequencyType){
            case "0":
                endTime = LocalDateTime.of(date, LocalTime.MAX);
                break;
            case "1":
                endTime = LocalDateTime.of(date.with(DayOfWeek.SUNDAY), LocalTime.MAX);
                break;
            case "2":
                endTime = LocalDateTime.of(date.with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
                break;
            case "3":
                int quarter = (date.getMonthValue() - 1) / 3;
                int lastMonthOfQuarter = quarter * 3 + 3;
                LocalDate lastDayOfQuarter = LocalDate.of(date.getYear(), lastMonthOfQuarter, 1).with(TemporalAdjusters.lastDayOfMonth());
                endTime = LocalDateTime.of(lastDayOfQuarter, LocalTime.MAX);
                break;
            case "4":
                endTime= LocalDateTime.of(LocalDate.of(date.getYear(), 12, 31), LocalTime.MAX);
                break;
            default:
                throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
        }
        return java.sql.Timestamp.valueOf(endTime);
    }

}
