package org.xyzh.crontab.scheduler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.xyzh.common.dto.crontab.TbCrontabLog;
import org.xyzh.common.dto.crontab.TbCrontabTask;
import org.xyzh.common.utils.IDUtils;
import org.xyzh.crontab.mapper.CrontabLogMapper;
import org.xyzh.crontab.pojo.TaskParams;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @description 任务执行器
 * @filename TaskExecutor.java
 * @author yslg
 * @copyright xyzh
 * @since 2025-10-25
 */
@Component("crontabTaskExecutor")
public class TaskExecutor {

    private static final Logger logger = LoggerFactory.getLogger(TaskExecutor.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private CrontabLogMapper logMapper;

    /**
     * @description 执行任务
     * @param task 任务对象
     * @author yslg
     * @since 2025-10-25
     */
    public void executeTask(TbCrontabTask task) {
        Date startTime = new Date();
        TbCrontabLog log = new TbCrontabLog();
        log.setID(IDUtils.generateID());
        log.setTaskId(task.getTaskId());
        log.setTaskName(task.getTaskName());
        log.setTaskGroup(task.getTaskGroup());
        log.setBeanName(task.getBeanName());
        log.setMethodName(task.getMethodName());
        log.setMethodParams(task.getMethodParams());
        log.setStartTime(startTime);
        log.setCreateTime(new Date());
        log.setDeleted(false);

        try {
            log.setExecuteStatus(0);
            log.setExecuteMessage("执行中");
            int i = logMapper.insertLog(log);

            // 检查是否允许并发执行
            if (task.getConcurrent() == 0) {
                // TODO: 可以添加分布式锁来防止并发执行
            }

            // 获取Bean实例
            Object bean = applicationContext.getBean(task.getBeanName());

            // 注入taskId和logId到参数中（适用于所有BaseTask子类）
            String methodParams = injectTaskContext(bean, task, log);

            // 获取并调用方法
            Method method;
            if (methodParams != null && !methodParams.isEmpty()) {
                method = bean.getClass().getMethod(task.getMethodName(), String.class);
                method.invoke(bean, methodParams);
            } else {
                method = bean.getClass().getMethod(task.getMethodName());
                method.invoke(bean);
            }

            // 执行成功
            Date endTime = new Date();
            log.setEndTime(endTime);
            log.setExecuteDuration((int) (endTime.getTime() - startTime.getTime()));
            log.setExecuteStatus(1);
            log.setExecuteMessage(null);

            logger.info("任务执行成功: {} [{}ms]", task.getTaskName(), log.getExecuteDuration());
        } catch (Exception e) {
            // 执行失败
            Date endTime = new Date();
            log.setEndTime(endTime);
            log.setExecuteDuration((int) (endTime.getTime() - startTime.getTime()));
            log.setExecuteStatus(0);
            log.setExecuteMessage("执行失败");
            log.setExceptionInfo(getExceptionStackTrace(e));

            logger.error("任务执行失败: {}", task.getTaskName(), e);
        } finally {
            // 保存执行日志
            try {
                logMapper.updateLog(log);
            } catch (Exception e) {
                logger.error("保存任务执行日志失败: {}", task.getTaskName(), e);
            }
        }
    }

    /**
     * @description 获取异常堆栈信息
     * @param e 异常
     * @return String
     * @author yslg
     * @since 2025-10-25
     */
    private String getExceptionStackTrace(Exception e) {
        StringBuilder sb = new StringBuilder();
        sb.append(e.getClass().getName()).append(": ").append(e.getMessage()).append("\n");
        
        StackTraceElement[] stackTrace = e.getStackTrace();
        int limit = Math.min(stackTrace.length, 10); // 只保留前10行
        
        for (int i = 0; i < limit; i++) {
            sb.append("\tat ").append(stackTrace[i].toString()).append("\n");
        }
        
        if (stackTrace.length > limit) {
            sb.append("\t... ").append(stackTrace.length - limit).append(" more\n");
        }

        return sb.toString();
    }

    /**
     * @description 注入任务上下文（taskId和logId）到参数中
     * @param bean Bean实例
     * @param task 任务对象
     * @param log 日志对象
     * @return 注入后的参数字符串
     */
    private String injectTaskContext(Object bean, TbCrontabTask task, TbCrontabLog log) {
        String methodParams = task.getMethodParams();

        if (bean instanceof org.xyzh.crontab.task.BaseTask) {
            try {
                // 从task对象构建完整的TaskParams
                TaskParams taskParams = new TaskParams();
                taskParams.setTaskGroup(task.getTaskGroup());      // 从task表获取
                taskParams.setMethodName(task.getMethodName());    // 从task表获取
                
                // 将methodParams解析为Map并设置到params字段
                Map<String, Object> params = JSON.parseObject(methodParams, 
                    new TypeReference<Map<String, Object>>(){});
                
                // 注入taskId、logId和metaId
                params.put("taskId", task.getTaskId());
                params.put("logId", log.getID());
                if (task.getMetaId() != null) {
                    params.put("metaId", task.getMetaId());
                }
                
                taskParams.setParams(params);
                
                methodParams = taskParams.toJson();
            } catch (Exception e) {
                logger.warn("构建TaskParams失败: {}", e.getMessage());
            }
        }
    
        return methodParams;
    }
}

