/**
 * XXL-JOB 执行器配置项
 */
interface XxlJobConfig {
    /** 执行器唯一标识(建议开发环境用不同名称避免冲突) */
    executorKey: string;
    /** 调度中心地址 */
    scheduleCenterUrl?: string;
    /** 认证令牌 */
    accessToken?: string;
    /** 任务日志存储路径 */
    jobLogPath?: string;
    /** 是否启用调试日志 */
    enableDebugLog?: boolean;
}

/**
 * 执行器全局配置
 */
interface JobExecutorOptions {
    /** Express/Koa 应用实例 */
    app: any; // Express.Application | Koa.Application;
    /** 应用对外访问的域名(用于回调) */
    appDomain: string;
    /** 应用监听端口 */
    port: number;
    /** XXL-JOB 核心配置 */
    xxlJob: XxlJobConfig;
    /** frpc 配置文件路径(可选) */
    frpcConfigPath?: string;
}

/**
 * XXL-JOB 任务执行器
 * 1. 注册任务处理器
 * 2. 启动 HTTP 服务
 * 3. 可选启动 frpc 内网穿透
 */
export class JobExecutor {
    private jobHandlers = new Map<string, Function>();

    constructor(opts: JobExecutorOptions) {
        // 初始化 frpc 内网穿透(如果配置存在)
        if (opts.frpcConfigPath) {
            require("./xxl-job/frpc").createFrpc(opts.frpcConfigPath);
        }

        // 连接 XXL-JOB 调度中心
        new (require("./xxl-job/index").XxlJobExecutor)(
            opts.xxlJob,
            this.jobHandlers
        ).applyMiddleware({
            app: opts.app,
            domain: opts.appDomain
        });

        // 启动 HTTP 服务
        opts.app.listen(opts.port, () => {
            console.log(`[XXL-JOB] Executor "${opts.xxlJob.executorKey}" is running`);
            console.log(`- Local: http://localhost:${opts.port}`);
            console.log(`- Public: ${opts.appDomain}`);
        });
    }

    /**
     * 注册任务
     * @param jobName 任务名称(需与调度中心配置一致)
     * @param handler 任务处理函数,需返回 Promise
     * @throws 如果任务已注册或 handler 不是函数
     */
    public register(jobName: string, handler: Function) {
        if (this.jobHandlers.has(jobName)) {
            throw new Error(`Job handler "${jobName}" is already registered`);
        }

        if (typeof handler !== 'function') {
            throw new Error(`Job handler "${jobName}" must be a function`);
        }

        console.log(`[XXL-JOB] Job "${jobName}" registered`);

        // 包装 handler
        this.jobHandlers.set(jobName, async (...args: any[]) => {
            try {
                return await handler(...args);
            } catch (error) {
                console.error(`[XXL-JOB] Job "${jobName}" failed:`, error);
                return { error };
            }
        });
    }

    /**
     * 获取任务
     * @param jobName 任务名称(需与调度中心配置一致)
     */
    public getJobHandler(jobName: string) {
        return this.jobHandlers.get(jobName);
    }
}