package com.kym.miniapp.jobs;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.entity.miniapp.ChargeOrder;
import com.kym.entity.miniapp.delay.DelayChargeOrder;
import com.kym.service.jobs.DelayService;
import com.kym.service.jobs.DelayedItem;
import com.kym.service.miniapp.ChargeOrderService;
import com.kym.service.miniapp.ChargeService;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author skyline
 * @description 停止充电延迟任务（预约充电）
 * @date 2023-10-08 22:11
 */
@Component("StopChargeDelayJob")
@Slf4j
@DS("db-miniapp")
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置成单例
public class StopChargeDelayJob implements DelayService<DelayChargeOrder> {


    /**
     * 预约订单队列
     */
    private final static DelayQueue<DelayedItem<DelayChargeOrder>> STOP_DELAY_QUEUE = new DelayQueue<>();
    private final ChargeOrderService chargeOrderService;
    private final ChargeService chargeService;
    /**
     * 线程池
     */
    private final static int MAX_THREAD_COUNT = 3;
    /**
     * Java 21 虚拟线程池
     * 替代传统的ThreadPoolExecutor，提供更好的I/O密集型任务性能
     */
    private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

    public StopChargeDelayJob(ChargeOrderService chargeOrderService, ChargeService chargeService) {
        this.chargeOrderService = chargeOrderService;
        this.chargeService = chargeService;
    }

    @DS("db-miniapp")
    // 这里不能使用@PostConstruct，在初始化完成后, bean 进入增强阶段, 所以这个阶段的任何AOP都是无效的,https://www.cnblogs.com/eternityz/p/15330069.html
    @EventListener(classes = {ContextRefreshedEvent.class}, id = "StopChargeDelayJob")
    @Async
    public void init() {

        // 队列加载所有充电状态为预约中且有结束时间的订单，按照结束时间排序
        var orderList = chargeOrderService.lambdaQuery()
                .eq(ChargeOrder::getOrderStatus, ChargeOrder.ORDER_STATUS_未知)
                .in(ChargeOrder::getChargeStatus, ChargeOrder.CHARGE_STATUS_预约中, ChargeOrder.CHARGE_STATUS_启动中, ChargeOrder.CHARGE_STATUS_充电中)
                .isNotNull(ChargeOrder::getEndTime)
                .orderByAsc(ChargeOrder::getEndTime)
                .list();

        var delayChargeOrderList = orderList.stream().map(o -> new DelayChargeOrder()
                        .setStartChargeSeq(o.getStartChargeSeq())
                        .setUserId(o.getUserId())
                        .setConnectorId(o.getConnectorId())
                        .setStartTime(o.getStartTime())
                        .setEndTime(o.getEndTime())
                        .setChargeStatus(o.getChargeStatus()))
                .toList();
        var delayList = delayChargeOrderList.stream().map(delay -> new DelayedItem<>(delay, delay.getEndTime())).toList();
        STOP_DELAY_QUEUE.addAll(delayList);

        processDelayedOrders();
    }

    private void processDelayedOrders() {
        // 启动线程池中的所有线程处理延迟队列
        for (int i = 0; i < MAX_THREAD_COUNT; i++) {
            executor.execute(() -> {
                ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> null); // 初始化为空值，避免使用new ThreadLocal()
                log.info("预约停止充电处理线程：{}", Thread.currentThread().getName());
                while (true) {
                    DelayedItem<DelayChargeOrder> delayedItem = null;
                    try {
                        // 阻塞等待延迟队列中的任务
                        delayedItem = STOP_DELAY_QUEUE.take();
                        log.info("出队预约停止充电订单：{}, 队列剩余：{}", delayedItem.data.getStartChargeSeq(), STOP_DELAY_QUEUE.size());
                        // 停止充电
                        var order = delayedItem.data;
                        threadLocal.set(order.getStartChargeSeq());
                        // 查询该设备最新订单是否是当前预约订单，是则按计划停止（优化：使用LIMIT 1代替查询所有）
                        var currentChargeOrder = chargeOrderService.lambdaQuery()
                                .eq(ChargeOrder::getConnectorId, order.getConnectorId())
                                .orderByDesc(ChargeOrder::getCreateTime)
                                .last("LIMIT 1")
                                .one();
                        if (currentChargeOrder.getStartChargeSeq().equals(order.getStartChargeSeq())) {
                            chargeService.queryStopCharge(order.getUserId(), order.getConnectorId());
                            log.info("预约充电停止成功:用户:{}, 订单号：{}, 预约停止时间：{}", order.getUserId(), order.getStartChargeSeq(), order.getEndTime());

                        } else {
                            log.error("预约充电停止异常:订单不匹配：原订单：{}, 当前设备最新订单：{}", order.getStartChargeSeq(), currentChargeOrder.getStartChargeSeq());
                            throw new BusinessException("预约充电停止异常");
                        }

                    } catch (Exception e) {
                        if (e instanceof InterruptedException) {
                            log.error("预约停止充电队列take异常", e);
                            // 线程被中断，退出循环
                            break;
                        } else {
                            log.info("预约停止充电失败，订单号：{}", threadLocal.get(), e);
                        }
                    } finally {
                        threadLocal.remove();
                    }
                }
            });
        }
    }

    @Override
    public boolean addToDelayQueue(DelayedItem<DelayChargeOrder> delayedItem) {
        return STOP_DELAY_QUEUE.add(delayedItem);
    }

    @Override
    public boolean addToDelayQueue(DelayChargeOrder delayChargeOrder) {
        DelayedItem<DelayChargeOrder> orderDelayed = new DelayedItem<>(delayChargeOrder, delayChargeOrder.getEndTime());
        return STOP_DELAY_QUEUE.add(orderDelayed);
    }

    @Override
    public boolean removeFromDelayQueue(Object startChargeSeq) {
        if (CommUtil.isEmptyOrNull(startChargeSeq)) {
            return false;
        }
        return STOP_DELAY_QUEUE.removeIf(queue -> queue.data.getStartChargeSeq().equals(startChargeSeq));
    }

    @PreDestroy
    public void destroy() {
        log.info("正在关闭停止充电延迟任务线程池...");
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
        log.info("停止充电延迟任务线程池已关闭");
        // 清理队列
        STOP_DELAY_QUEUE.clear();
    }
}
