package link.at17.mid.tushare.component;

import java.util.Collection;
import java.util.List;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import com.baomidou.mybatisplus.core.conditions.Wrapper;

import link.at17.mid.tushare.data.models.UpdatePlan;
import link.at17.mid.tushare.service.UpdatePlanService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 更新计划同步切片器<p>
 * 主要用于拦截通过 updatePlanService 下的所有 save/update/remove 方法
 * <p>注意：
 * <ol>
 * <li>仅限第一个参数是 {@code UpdatePlan}, {@code Collection<UpdatePlan>} 或 {@code Wrapper<UpdatePlan>} 的方法
 * <li>由于切片是对 UpdatePlanService 切片, 但切片器内部又调用了 UpdatePlanService,
 * 如果调用的方法恰好是被切片的方法, 则会进入调用递归导致栈溢出, 需要额外注意.
 */
@Aspect
@Component
@Slf4j
@Validated
@RequiredArgsConstructor
public class UpdatePlanSyncAspect {

    private final UpdatePlanService updatePlanService;

    // 拦截所有 UpdatePlanService 的 save/update/remove 方法
    @Pointcut("(" + 
            "execution(* com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.save*(..)) || " +
            "execution(* com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.update*(..)) || " +
            "execution(* com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.remove*(..)) || " +
            "execution(* com.baomidou.mybatisplus.extension.service.IService.save*(..)) || " +
            "execution(* com.baomidou.mybatisplus.extension.service.IService.update*(..)) || " +
            "execution(* com.baomidou.mybatisplus.extension.service.IService.remove*(..))" +
            ") && " +
            "target(link.at17.mid.tushare.service.UpdatePlanService)")
    public void taskOps() {}

    @AfterReturning(pointcut = "taskOps()", returning = "result")
    public void afterTaskOp(JoinPoint jp, Object result) {
        
        if (!(result instanceof Boolean bool) || !bool) {
            // 未成功执行，不进行切片
            return;
        }
        
        Object arg = jp.getArgs()[0];
        if (arg instanceof UpdatePlan task) {
            handle(task, jp.getSignature().getName());
        }
        else if (arg instanceof Collection<?> coll) {
            coll.stream()
                .filter(UpdatePlan.class::isInstance)
                .map(UpdatePlan.class::cast)
                .forEach(t -> handle(t, jp.getSignature().getName()));
        }
        else if (arg instanceof Wrapper<?> wrapper) {
            @SuppressWarnings("unchecked")
            List<UpdatePlan> plans = updatePlanService.list((Wrapper<UpdatePlan>)wrapper);
            plans.stream()
            .filter(UpdatePlan.class::isInstance)
            .map(UpdatePlan.class::cast)
            .forEach(t -> handle(t, jp.getSignature().getName()));
        }
    }

    private void handle(UpdatePlan task, String methodName) {
        
        // 但凡存在的都应该 reschedule
        
        try {
            if (methodName.startsWith("remove")) {
                updatePlanService.deleteTask(task.getId());
                log.info("已同步删除 Quartz 任务: [{}]{}", task.getId(), task.getName());
            }
            else {
                updatePlanService.rescheduleTask(task);
                log.info("已同步更新 Quartz 任务: [{}]{}", task.getId(), task.getName());
            }
        }
        catch (Exception e) {
            log.error("同步 Quartz 调度失败: [{}]{}", task.getId(), task.getName(), e);
        }
    }
}
