import fp from 'fastify-plugin';
import { fetchTweets, fetchAllTweets, fetchProfile as fetchNitterProfile, extractTitle, extractYoutubeVideoIds, extractProfile } from './scraper.js';
import { fetchVideoInfo } from '../youtube/api.js';
import { formatDate, formatTime, nowKST } from '../../utils/date.js';
import { withTransaction } from '../../utils/transaction.js';
import { syncScheduleById } from '../meilisearch/index.js';
import { logActivity } from '../../utils/log.js';

const X_CATEGORY_ID = 3;
const YOUTUBE_CATEGORY_ID = 2;
const PROFILE_CACHE_PREFIX = 'x_profile:';
const PROFILE_TTL = 604800; // 7일

async function xBotPlugin(fastify, opts) {
  /**
   * 관리 중인 YouTube 채널 ID 목록 (DB에서 조회)
   */
  async function getManagedChannelIds() {
    const [rows] = await fastify.db.query(
      'SELECT channel_id FROM bot_youtube WHERE enabled = 1'
    );
    return rows.map(r => r.channel_id);
  }

  /**
   * X 프로필 저장 (bot_x 테이블 + Redis 캐시)
   */
  async function saveProfile(username, profile) {
    if (!profile.displayName && !profile.avatarUrl) return;

    // bot_x 테이블 업데이트
    await fastify.db.query(`
      UPDATE bot_x SET display_name = ?, avatar_url = ?
      WHERE username = ?
    `, [profile.displayName, profile.avatarUrl, username]);

    // Redis 캐시에도 저장
    const data = {
      username,
      displayName: profile.displayName,
      avatarUrl: profile.avatarUrl,
      updatedAt: nowKST(),
    };
    await fastify.redis.setex(
      `${PROFILE_CACHE_PREFIX}${username}`,
      PROFILE_TTL,
      JSON.stringify(data)
    );
  }

  /**
   * 트윗을 DB에 저장
   */
  async function saveTweet(tweet, username) {
    // 중복 체크 (post_id로) - 트랜잭션 전에 수행
    const [existing] = await fastify.db.query(
      'SELECT id FROM schedule_x WHERE post_id = ?',
      [tweet.id]
    );
    if (existing.length > 0) {
      return null;
    }

    const date = formatDate(tweet.time);
    const time = formatTime(tweet.time);
    const title = extractTitle(tweet.text);

    // 리트윗인 경우 원본 작성자를 username으로 사용
    const tweetUsername = tweet.originalUsername || username;

    // 트랜잭션으로 INSERT 작업 수행
    return withTransaction(fastify.db, async (connection) => {
      // schedules 테이블에 저장
      const [result] = await connection.query(
        'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
        [X_CATEGORY_ID, title, date, time]
      );
      const scheduleId = result.insertId;

      // schedule_x 테이블에 저장
      await connection.query(
        'INSERT INTO schedule_x (schedule_id, post_id, username, content, image_urls) VALUES (?, ?, ?, ?, ?)',
        [
          scheduleId,
          tweet.id,
          tweetUsername,
          tweet.text,
          tweet.imageUrls.length > 0 ? JSON.stringify(tweet.imageUrls) : null,
        ]
      );

      return scheduleId;
    });
  }

  /**
   * YouTube 영상을 DB에 저장 (트윗에서 감지된 링크)
   */
  async function saveYoutubeFromTweet(video) {
    // 중복 체크 - 트랜잭션 전에 수행
    const [existing] = await fastify.db.query(
      'SELECT id FROM schedule_youtube WHERE video_id = ?',
      [video.videoId]
    );
    if (existing.length > 0) {
      return null;
    }

    // 트랜잭션으로 INSERT 작업 수행
    try {
      return await withTransaction(fastify.db, async (connection) => {
        // schedules 테이블에 저장
        const [result] = await connection.query(
          'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
          [YOUTUBE_CATEGORY_ID, video.title, video.date, video.time]
        );
        const scheduleId = result.insertId;

        // schedule_youtube 테이블에 저장
        await connection.query(
          'INSERT INTO schedule_youtube (schedule_id, video_id, video_type, channel_id, channel_name) VALUES (?, ?, ?, ?, ?)',
          [scheduleId, video.videoId, video.videoType, video.channelId, video.channelTitle]
        );

        return scheduleId;
      });
    } catch (err) {
      // UNIQUE 제약 위반 (동시성 중복) → 무시
      if (err.code === 'ER_DUP_ENTRY') return null;
      throw err;
    }
  }

  /**
   * 트윗에서 YouTube 링크 처리
   */
  async function processYoutubeLinks(tweet) {
    const videoIds = extractYoutubeVideoIds(tweet.text);
    if (videoIds.length === 0) return 0;

    const managedChannels = await getManagedChannelIds();
    let addedCount = 0;

    for (const videoId of videoIds) {
      try {
        const video = await fetchVideoInfo(videoId);
        if (!video) continue;

        // 관리 중인 채널이면 스킵
        if (managedChannels.includes(video.channelId)) continue;

        const scheduleId = await saveYoutubeFromTweet(video);
        if (scheduleId) {
          // Meilisearch 동기화
          await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
          addedCount++;
        }
      } catch (err) {
        fastify.log.error(`YouTube 영상 처리 오류 (${videoId}): ${err.message}`);
      }
    }

    return addedCount;
  }

  /**
   * 텍스트 필터 적용 (키워드 중 하나라도 포함되면 true)
   */
  function matchesFilter(text, filters) {
    if (!filters || filters.length === 0) return true;
    const lowerText = text.toLowerCase();
    return filters.some(filter => lowerText.includes(filter.toLowerCase()));
  }

  /**
   * 최근 트윗 동기화 (정기 실행)
   */
  async function syncNewTweets(bot) {
    const options = { includeRetweets: bot.includeRetweets || false };
    const { tweets, profile } = await fetchTweets(bot.nitterUrl, bot.username, options);

    // 프로필 저장 (DB + 캐시)
    await saveProfile(bot.username, profile);

    let addedCount = 0;
    let ytAddedCount = 0;

    for (const tweet of tweets) {
      // 텍스트 필터 적용
      if (!matchesFilter(tweet.text, bot.textFilters)) {
        continue;
      }

      const scheduleId = await saveTweet(tweet, bot.username);
      if (scheduleId) {
        // Meilisearch 동기화
        await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
        const title = extractTitle(tweet.text);
        logActivity(fastify.db, {
          actor: bot.id,
          action: 'create',
          category: 'schedule',
          targetType: 'x_schedule',
          targetId: scheduleId,
          summary: `X 트윗 추가: ${title}`,
        });
        addedCount++;
        // YouTube 링크 처리 (옵션이 켜져 있을 때만)
        if (bot.extractYoutube === true) {
          ytAddedCount += await processYoutubeLinks(tweet);
        }
      }
    }

    return { addedCount: addedCount + ytAddedCount, total: tweets.length, tweetCount: addedCount, ytCount: ytAddedCount };
  }

  /**
   * 전체 트윗 동기화 (초기화)
   */
  async function syncAllTweets(bot) {
    const options = { includeRetweets: bot.includeRetweets || false };
    const tweets = await fetchAllTweets(bot.nitterUrl, bot.username, fastify.log, options);

    let addedCount = 0;
    let ytAddedCount = 0;

    for (const tweet of tweets) {
      // 텍스트 필터 적용
      if (!matchesFilter(tweet.text, bot.textFilters)) {
        continue;
      }

      const scheduleId = await saveTweet(tweet, bot.username);
      if (scheduleId) {
        // Meilisearch 동기화
        await syncScheduleById(fastify.meilisearch, fastify.db, scheduleId);
        addedCount++;
        // YouTube 링크 처리 (옵션이 켜져 있을 때만)
        if (bot.extractYoutube === true) {
          ytAddedCount += await processYoutubeLinks(tweet);
        }
      }
    }

    return { addedCount: addedCount + ytAddedCount, total: tweets.length, tweetCount: addedCount, ytCount: ytAddedCount };
  }

  /**
   * X 프로필 조회 (Redis 캐시 → bot_x 테이블 → Nitter 직접 조회)
   */
  async function getProfile(username) {
    // Redis 캐시 확인
    const cached = await fastify.redis.get(`${PROFILE_CACHE_PREFIX}${username}`);
    if (cached) {
      return JSON.parse(cached);
    }

    // bot_x 테이블에서 조회
    const [rows] = await fastify.db.query(
      'SELECT username, display_name, avatar_url FROM bot_x WHERE username = ?',
      [username]
    );

    if (rows.length > 0) {
      const row = rows[0];
      const data = {
        username: row.username,
        displayName: row.display_name,
        avatarUrl: row.avatar_url,
      };
      await fastify.redis.setex(
        `${PROFILE_CACHE_PREFIX}${username}`,
        PROFILE_TTL,
        JSON.stringify(data)
      );
      return data;
    }

    // bot_x에 없으면 Nitter에서 직접 조회 (리트윗 원본 작성자 등)
    try {
      const nitterUrl = fastify.config?.nitter?.url || process.env.NITTER_URL || 'http://nitter:8080';
      const profile = await fetchNitterProfile(nitterUrl, username);
      if (profile) {
        const data = {
          username: profile.username,
          displayName: profile.displayName,
          avatarUrl: profile.avatarUrl,
        };
        // Redis 캐시에 저장
        await fastify.redis.setex(
          `${PROFILE_CACHE_PREFIX}${username}`,
          PROFILE_TTL,
          JSON.stringify(data)
        );
        return data;
      }
    } catch (err) {
      fastify.log.error(`Nitter 프로필 조회 실패 (${username}): ${err.message}`);
    }

    return null;
  }

  fastify.decorate('xBot', {
    syncNewTweets,
    syncAllTweets,
    getProfile,
  });
}

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