package com.kym.admin.jobs;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.kym.common.utils.CommUtil;
import com.kym.entity.admin.*;
import com.kym.entity.admin.delay.DelayActivity;
import com.kym.service.admin.*;
import com.kym.service.jobs.DelayService;
import com.kym.service.jobs.DelayedItem;
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.stereotype.Service;

import java.util.Comparator;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

/**
 * @author skyline
 * @description 活动启闭延迟任务
 * @date 2023-10-08 22:11
 */
@Service
@Slf4j
@DS("db-admin")
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) // 设置成单例
public class ActivityDelayJob implements DelayService<DelayActivity> {

    /**
     * 活动队列
     */
    private final static DelayQueue<DelayedItem<DelayActivity>> DELAY_QUEUE = new DelayQueue<>();

    /**
     * 队列最大容量
     */
    private static final int MAX_QUEUE_SIZE = 10000;

    /**
     * 控制线程运行状态的标志位
     */
    private volatile boolean running = true;

    private final ActivityService activityService;
    private final ActivityStationService activityStationService;
    private final RechargeRightsService rechargeRightsService;

    private final CouponService couponService;
    private final BannerService bannerService;
    /**
     * 线程池
     */
    private final ExecutorService executor = Executors.newFixedThreadPool(1);

    public ActivityDelayJob(ActivityService activityService, ActivityStationService activityStationService, RechargeRightsService rechargeRightsService, CouponService couponService, BannerService bannerService) {
        this.activityService = activityService;
        this.activityStationService = activityStationService;
        this.rechargeRightsService = rechargeRightsService;
        this.couponService = couponService;
        this.bannerService = bannerService;
    }

    @DS("db-admin")
    // 这里不能使用@PostConstruct，在初始化完成后, bean 进入增强阶段, 所以这个阶段的任何AOP都是无效的,https://www.cnblogs.com/eternityz/p/15330069.html
    @EventListener
    public void init(ContextRefreshedEvent event) {
        // 队列加载所有进行中和未开始的活动
        var activity = activityService.lambdaQuery().in(Activity::getStatus, Activity.STATUS_进行中, Activity.STATUS_未开始).list();

        // 启动队列
        var delayStartActivityList = activity.stream().filter(a -> a.getStatus().equals(Activity.STATUS_未开始)).map(act -> new DelayActivity()
                        .setId(act.getId())
                        .setName(act.getName())
                        .setExecuteTime(act.getStartTime())
                        .setDiscountType(act.getDiscountType())
                        .setType(DelayActivity.TYPE_启动))
                .toList();

        // 结束队列
        var delayEndActivityList = activity.stream().map(act -> new DelayActivity()
                        .setId(act.getId())
                        .setName(act.getName())
                        .setExecuteTime(act.getEndTime())
                        .setDiscountType(act.getDiscountType())
                        .setType(DelayActivity.TYPE_结束))
                .toList();

        // 启动队列和结束队列合并之后按照时间排序加入队列
        Stream.concat(delayStartActivityList.stream(), delayEndActivityList.stream())
                .sorted(Comparator.comparing(DelayActivity::getExecuteTime))
                .map(delay -> new DelayedItem<>(delay, delay.getExecuteTime())).toList().forEach(this::addToDelayQueue);

        // 开启线程处理队列消息
        executor.execute(() -> {
            ThreadLocal<Long> threadLocal = new ThreadLocal<>();
            log.info("活动延迟启闭处理线程：{}", Thread.currentThread().getName());
            DelayedItem<DelayActivity> delayedItem;
            while (running) {
                try {
                    delayedItem = DELAY_QUEUE.take();
                    if (!running) {
                        break;
                    }
                    var delayActivity = delayedItem.data;
                    threadLocal.set(delayActivity.getId());
                    if (delayActivity.getType().equals(DelayActivity.TYPE_启动)) {
                        log.info("活动id：{},活动名：{}启动中...", delayActivity.getId(), delayActivity.getName());
                        // 开始活动
                        // 修改活动状态为进行中
                        activityService.lambdaUpdate().set(Activity::getStatus, Activity.STATUS_进行中).eq(Activity::getId, delayActivity.getId()).update();
                        // 修改活动站点关联表状态为进行中
                        activityStationService.lambdaUpdate().set(ActivityStation::getStatus, Activity.STATUS_进行中).eq(ActivityStation::getActivityId, delayActivity.getId()).update();

                        // 判断活动优惠类型，分别处理活动启动逻辑
                        if (delayActivity.getDiscountType().equals(Activity.DISCOUNT_TYPE_服务费折扣权益)) {
                            // 修改充值权益为进行中
                            rechargeRightsService.lambdaUpdate().set(RechargeRights::getStatus, RechargeRights.STATUS_进行中).eq(RechargeRights::getActivityId, delayActivity.getId()).update();
                        }
                        if (delayActivity.getDiscountType().equals(Activity.DISCOUNT_TYPE_优惠券)) {
                            // 修改优惠券状态为进行中
                            couponService.lambdaUpdate().set(Coupon::getStatus, Coupon.STATUS_进行中).eq(Coupon::getActivityId, delayActivity.getId()).update();
                        }

                        // 修改banner状态为有效
                        bannerService.lambdaUpdate().set(Banner::getStatus, Banner.STATUS_有效).eq(Banner::getActivityId, delayActivity.getId()).update();
                        log.info("活动id：{},活动名：{}已开始...", delayActivity.getId(), delayActivity.getName());
                    } else {
                        log.info("活动id：{},活动名：{}停止中...", delayActivity.getId(), delayActivity.getName());
                        // 停止活动
                        // 修改活动状态为已结束
                        activityService.lambdaUpdate().set(Activity::getStatus, Activity.STATUS_已结束).eq(Activity::getId, delayActivity.getId()).update();
                        // 修改活动站点关联表状态为已结束
                        activityStationService.lambdaUpdate().set(ActivityStation::getStatus, Activity.STATUS_已结束).eq(ActivityStation::getActivityId, delayActivity.getId()).update();

                        // 判断活动优惠类型，分别处理活动结束逻辑
                        if (delayActivity.getDiscountType().equals(Activity.DISCOUNT_TYPE_服务费折扣权益)) {
                            // 修改充值权益为已结束
                            rechargeRightsService.lambdaUpdate().set(RechargeRights::getStatus, RechargeRights.STATUS_已结束).eq(RechargeRights::getActivityId, delayActivity.getId()).update();
                        }
                        if (delayActivity.getDiscountType().equals(Activity.DISCOUNT_TYPE_优惠券)) {
                            // 修改优惠券状态为已结束
                            couponService.lambdaUpdate().set(Coupon::getStatus, Coupon.STATUS_已结束).eq(Coupon::getActivityId, delayActivity.getId()).update();
                        }

                        // 修改banner状态为失效
                        bannerService.lambdaUpdate().set(Banner::getStatus, Banner.STATUS_无效).eq(Banner::getActivityId, delayActivity.getId()).update();
                        log.info("活动id：{},活动名：{}已结束...", delayActivity.getId(), delayActivity.getName());
                    }
                    // 线程休眠100ms
                    Thread.sleep(100);
                } catch (Exception e) {
                    if (e instanceof InterruptedException) {
                        log.info("活动延迟任务线程被中断，准备退出");
                        Thread.currentThread().interrupt();
                        break;
                    } else {
                        log.error("活动到期停止处理异常，主活动id：{}", threadLocal.get(), e);
                    }
                } finally {
                    threadLocal.remove();
                }
            }
            log.info("活动延迟启闭处理线程已退出");
        });

    }

    @Override
    public boolean addToDelayQueue(DelayedItem<DelayActivity> delayedItem) {
        if (DELAY_QUEUE.size() >= MAX_QUEUE_SIZE) {
            log.error("活动延迟队列已满，拒绝添加任务，当前大小: {}", DELAY_QUEUE.size());
            return false;
        }
        return DELAY_QUEUE.add(delayedItem);
    }

    @Override
    public boolean addToDelayQueue(DelayActivity activity) {
        if (DELAY_QUEUE.size() >= MAX_QUEUE_SIZE) {
            log.error("活动延迟队列已满，拒绝添加任务，当前大小: {}", DELAY_QUEUE.size());
            return false;
        }
        DelayedItem<DelayActivity> activityDelayed = new DelayedItem<>(activity, activity.getExecuteTime());
        return DELAY_QUEUE.add(activityDelayed);
    }


    @Override
    public boolean removeFromDelayQueue(Object activityId) {
        if (CommUtil.isEmptyOrNull(activityId)) {
            return false;
        }
        return DELAY_QUEUE.removeIf(queue -> queue.data.getId().equals(activityId));
    }

    /**
     * 应用关闭时释放资源
     */
    @jakarta.annotation.PreDestroy
    public void destroy() {
        log.info("正在关闭活动延迟任务线程池...");
        running = false;
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
        // 清理队列
        DELAY_QUEUE.clear();
        log.info("活动延迟任务线程池已关闭");
    }
}
