import fp from 'fastify-plugin';
import cron from 'node-cron';
import bots from '../config/bots.js';

const REDIS_PREFIX = 'bot:status:';

async function schedulerPlugin(fastify, opts) {
  const tasks = new Map();

  /**
   * 봇 상태 Redis에 저장
   */
  async function updateStatus(botId, status) {
    const current = await getStatus(botId);
    const updated = { ...current, ...status, updatedAt: new Date().toISOString() };
    await fastify.redis.set(`${REDIS_PREFIX}${botId}`, JSON.stringify(updated));
    return updated;
  }

  /**
   * 봇 상태 Redis에서 조회
   */
  async function getStatus(botId) {
    const data = await fastify.redis.get(`${REDIS_PREFIX}${botId}`);
    if (data) {
      return JSON.parse(data);
    }
    return {
      status: 'stopped',
      lastCheckAt: null,
      lastAddedCount: 0,
      totalAdded: 0,
      errorMessage: null,
    };
  }

  /**
   * 봇 동기화 함수 가져오기
   */
  function getSyncFunction(bot) {
    if (bot.type === 'youtube') {
      return fastify.youtubeBot.syncNewVideos;
    } else if (bot.type === 'x') {
      return fastify.xBot.syncNewTweets;
    }
    return null;
  }

  /**
   * 봇 시작
   */
  async function startBot(botId) {
    const bot = bots.find(b => b.id === botId);
    if (!bot) {
      throw new Error(`봇을 찾을 수 없습니다: ${botId}`);
    }

    // 기존 태스크가 있으면 정지
    if (tasks.has(botId)) {
      tasks.get(botId).stop();
      tasks.delete(botId);
    }

    const syncFn = getSyncFunction(bot);
    if (!syncFn) {
      throw new Error(`지원하지 않는 봇 타입: ${bot.type}`);
    }

    // cron 태스크 등록
    const task = cron.schedule(bot.cron, async () => {
      fastify.log.info(`[${botId}] 동기화 시작`);
      try {
        const result = await syncFn(bot);
        const status = await getStatus(botId);
        await updateStatus(botId, {
          status: 'running',
          lastCheckAt: new Date().toISOString(),
          lastAddedCount: result.addedCount,
          totalAdded: (status.totalAdded || 0) + result.addedCount,
          errorMessage: null,
        });
        fastify.log.info(`[${botId}] 동기화 완료: ${result.addedCount}개 추가`);
      } catch (err) {
        await updateStatus(botId, {
          status: 'error',
          lastCheckAt: new Date().toISOString(),
          errorMessage: err.message,
        });
        fastify.log.error(`[${botId}] 동기화 오류: ${err.message}`);
      }
    });

    tasks.set(botId, task);
    await updateStatus(botId, { status: 'running' });
    fastify.log.info(`[${botId}] 스케줄 시작 (cron: ${bot.cron})`);

    // 즉시 1회 실행
    try {
      const result = await syncFn(bot);
      const status = await getStatus(botId);
      await updateStatus(botId, {
        lastCheckAt: new Date().toISOString(),
        lastAddedCount: result.addedCount,
        totalAdded: (status.totalAdded || 0) + result.addedCount,
      });
      fastify.log.info(`[${botId}] 초기 동기화 완료: ${result.addedCount}개 추가`);
    } catch (err) {
      fastify.log.error(`[${botId}] 초기 동기화 오류: ${err.message}`);
    }
  }

  /**
   * 봇 정지
   */
  async function stopBot(botId) {
    if (tasks.has(botId)) {
      tasks.get(botId).stop();
      tasks.delete(botId);
    }
    await updateStatus(botId, { status: 'stopped' });
    fastify.log.info(`[${botId}] 스케줄 정지`);
  }

  /**
   * 모든 활성 봇 시작
   */
  async function startAll() {
    for (const bot of bots) {
      if (bot.enabled) {
        try {
          await startBot(bot.id);
        } catch (err) {
          fastify.log.error(`[${bot.id}] 시작 실패: ${err.message}`);
        }
      }
    }
  }

  /**
   * 모든 봇 정지
   */
  async function stopAll() {
    for (const [botId, task] of tasks) {
      task.stop();
      await updateStatus(botId, { status: 'stopped' });
    }
    tasks.clear();
  }

  // 데코레이터 등록
  fastify.decorate('scheduler', {
    startBot,
    stopBot,
    startAll,
    stopAll,
    getStatus,
    getBots: () => bots,
  });

  // 앱 종료 시 모든 봇 정지
  fastify.addHook('onClose', async () => {
    await stopAll();
    fastify.log.info('모든 봇 스케줄 정지');
  });
}

export default fp(schedulerPlugin, {
  name: 'scheduler',
  dependencies: ['db', 'redis', 'youtubeBot', 'xBot'],
});
