package cn.iocoder.yudao.module.rescue.utils;

import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.appBase.domain.SysAnnouncement;
import cn.iocoder.yudao.module.appBase.service.ISysAnnouncementService;
import cn.iocoder.yudao.module.constant.UserConstants;
import cn.iocoder.yudao.module.rescue.dto.TaskDto;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.service.permission.RoleService;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Redisson 延迟队列组件，用于处理带有延迟时间的任务。
 *
 * @author 小李
 * @date 16:37 2024/8/26
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RedissonDelayQueue {

    @Resource
    private RedissonClient redissonClient;

    // 注入 Redisson 提供的延迟队列接口
    private RDelayedQueue<String> delayedQueue;

    // 注入 Redisson 提供的阻塞队列接口
    private RBlockingQueue<String> blockingQueue;

    // 任务索引，用于快速查找任务
    private RMap<Long, String> taskIndex;

    @Resource
    private ISysAnnouncementService announcementService;
    @Resource
    private RoleService roleService;
    @Resource
    private PermissionApi permissionService;

    /**
     * 初始化方法，在 Spring 容器加载后自动执行。
     * 创建一个固定大小的线程池来消费阻塞队列中的任务。
     */
    @PostConstruct
    public void init() {

        blockingQueue = redissonClient.getBlockingQueue("RESCUE_QUEUE");

        delayedQueue = redissonClient.getDelayedQueue(blockingQueue);

        taskIndex = redissonClient.getMap("RESCUE_INDEX");

        // 创建一个单线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(1);

        // 提交一个无限循环的任务到线程池，用于持续消费队列中的任务
        executorService.submit(() -> {
            while (true) {
                try {
                    // 从阻塞队列中获取一个任务，如果队列为空，则此方法会一直阻塞直到有任务可用
                    String task = blockingQueue.take();

                    // 记录接收到的任务信息
                    log.info("定时任务:{}", task);

                    // 通知发起者
                    TaskDto taskDto = JSON.parseObject(task, TaskDto.class);
                    SysAnnouncement sysAnnouncement = new SysAnnouncement();
                    sysAnnouncement.setType("救援超时信息");
                    sysAnnouncement.setToUserIds(Collections.singletonList(taskDto.getManageId()));
                    sysAnnouncement.setTitle("有救援订单超时请立即处理");
                    sysAnnouncement.setContent("有救援订单超时请立即处理");
                    announcementService.insertSysAnnouncements(sysAnnouncement);
                    TenantContextHolder.setIgnore(true);
                    // 发送订单信息给其他人 经理 总调度  将这俩人指定 救援其他通知人 角色
                    RoleDO roleInfo = roleService.getRoleByCodeWithoutTenant("rescueOtherNotify");
                    if(null!=roleInfo){
                        List<Long> userIds = permissionService.getUserIdByRoleId(roleInfo.getId());
                        if(!userIds.isEmpty()){
                            // 发消息
                            SysAnnouncement otherAnnouncement = new SysAnnouncement();
                            otherAnnouncement.setType("救援超时信息");
                            otherAnnouncement.setToUserIds(userIds);
                            otherAnnouncement.setTitle("有救援订单超时请立即处理");
                            otherAnnouncement.setContent("有救援订单超时请立即处理");
                            announcementService.insertSysAnnouncements(otherAnnouncement);
                        }
                    }
                    TenantContextHolder.setIgnore(false);
                    /** 两个值虽然一样，但避免无法删除一直存在问题，干脆重新来过 */
                    // 移除原来的
                    removeAllTasks(taskDto.getRescueInfoId());
                    // 在新创建定时任务
                    offerTask(task, UserConstants.RESCUE_TIME);
                } catch (Exception e) {
                    // 记录并打印异常信息
//                    log.error("发生错误", e);
                }
            }
        });
    }

    /**
     * 向延迟队列中添加一个任务，并设置延迟时间。
     *
     * @param task 要添加的任务
     * @param seconds 任务的延迟时间，单位为秒
     */
    public void offerTask(String task, long seconds) {
        // 记录添加任务的日志信息
        log.info("添加定时任务:{},过期时间为:{}s", task, seconds);

        // 从任务中提取 rescueInfoId
        TaskDto taskDto = JSON.parseObject(task, TaskDto.class);
        Long rescueInfoId = taskDto.getRescueInfoId();

        // 存储任务索引
        taskIndex.put(rescueInfoId, task);

        // 向延迟队列添加任务，并设置延迟时间
        delayedQueue.offer(task, seconds, TimeUnit.SECONDS);
    }

    /**
     * 从延迟队列中移除指定的任务。
     *
     * @param rescueInfoId 要移除的任务
     * @return 移除是否成功
     */
    private boolean removeDelayedTask(Long rescueInfoId) {
        try {
            // 从任务索引中查找任务
            String task = taskIndex.get(rescueInfoId);

            // 如果找到了任务，则尝试从延迟队列中移除任务
            if (task != null) {
                boolean removed = delayedQueue.remove(task);
                if (removed) {
                    // 从任务索引中移除任务
                    taskIndex.remove(rescueInfoId);
                }
                return removed;
            }
        } catch (Exception e) {
            log.error("延迟队列删除失败", e);
        }
        return false;
    }

    /**
     * 从阻塞队列中移除指定的任务。
     *
     * @param rescueInfoId 要移除的任务
     * @return 移除是否成功
     */
    private boolean removeBlockingTask(Long rescueInfoId) {
        try {
            // 从任务索引中查找任务
            String task = taskIndex.get(rescueInfoId);

            // 如果找到了任务，则尝试从阻塞队列中移除任务
            if (task != null) {
                boolean removed = blockingQueue.remove(task);
                if (removed) {
                    // 从任务索引中移除任务
                    taskIndex.remove(rescueInfoId);
                }
                return removed;
            }
        } catch (Exception e) {
            log.error("阻塞队列删除失败", e);
        }
        return false;
    }

    /**
     * 尝试同时从延迟队列和阻塞队列中移除指定的任务。
     * 如果任务在任何一个队列中存在，则尝试移除。
     *
     * @param rescueInfoId 要移除的任务
     * @return 是否至少在一个队列中移除成功
     */
    public boolean removeAllTasks(Long rescueInfoId) {
        // 尝试从延迟队列中移除任务
        boolean removedFromDelayed = removeDelayedTask(rescueInfoId);

        // 尝试从阻塞队列中移除任务
        boolean removedFromBlocking = removeBlockingTask(rescueInfoId);

        // 如果任何一个队列中移除成功，则返回 true
        return removedFromDelayed || removedFromBlocking;
    }
}
