/**
 * PPT 썸네일 작업 이벤트 로그 — PostgreSQL 우선, PG 없을 때만 data/thumbnail-events.json 폴백
 */
const fs = require("fs/promises");
const { v4: uuidv4 } = require("uuid");

/**
 * @param {import("pg").QueryResultRow} row
 */
function mapRowToEvent(row) {
  if (!row) return null;
  return {
    id: row.id,
    at: row.occurred_at ? new Date(row.occurred_at).toISOString() : null,
    type: row.event_type,
    lectureId: row.lecture_id || undefined,
    lectureTitle: row.lecture_title || undefined,
    reason: row.reason || undefined,
    force: row.force_flag === true,
    queueSizeAfter: row.queue_size_after != null ? Number(row.queue_size_after) : undefined,
    retryCount: row.retry_count != null ? Number(row.retry_count) : undefined,
    durationMs: row.duration_ms != null ? Number(row.duration_ms) : undefined,
    error: row.error_text || undefined,
  };
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} jsonPath
 */
async function readThumbnailEvents(pgPool, jsonPath) {
  if (pgPool) {
    const r = await pgPool.query(
      `SELECT id, occurred_at, event_type, lecture_id, lecture_title, reason, force_flag,
              queue_size_after, retry_count, duration_ms, error_text
       FROM lecture_thumbnail_events
       ORDER BY occurred_at ASC`
    );
    return (r.rows || []).map(mapRowToEvent);
  }
  try {
    const raw = await fs.readFile(jsonPath, "utf-8");
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? parsed : [];
  } catch {
    return [];
  }
}

/**
 * @param {import("pg").Pool} pgPool
 * @param {number} keep
 */
async function pruneThumbnailEvents(pgPool, keep) {
  const k = Math.max(Number(keep) || 200, 20);
  await pgPool.query(
    `DELETE FROM lecture_thumbnail_events a
     USING (
       SELECT id FROM (
         SELECT id, ROW_NUMBER() OVER (ORDER BY occurred_at DESC) AS rn
         FROM lecture_thumbnail_events
       ) t WHERE t.rn > $1
     ) d
     WHERE a.id = d.id`,
    [k]
  );
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} jsonPath
 * @param {number} keep
 * @param {object} payload
 */
async function appendThumbnailEvent(pgPool, jsonPath, keep, payload) {
  const id = uuidv4();
  const at = new Date().toISOString();
  if (pgPool) {
    await pgPool.query(
      `INSERT INTO lecture_thumbnail_events (
        id, occurred_at, event_type, lecture_id, lecture_title, reason, force_flag,
        queue_size_after, retry_count, duration_ms, error_text
      ) VALUES ($1, $2::timestamptz, $3, $4::uuid, $5, $6, $7, $8, $9, $10, $11)`,
      [
        id,
        at,
        payload.type || "unknown",
        payload.lectureId || null,
        payload.lectureTitle || null,
        payload.reason || null,
        payload.force === true,
        payload.queueSizeAfter != null ? payload.queueSizeAfter : null,
        payload.retryCount != null ? payload.retryCount : null,
        payload.durationMs != null ? payload.durationMs : null,
        payload.error || null,
      ]
    );
    await pruneThumbnailEvents(pgPool, keep);
    return;
  }
  const events = await readThumbnailEvents(null, jsonPath);
  events.push({ id, at, ...payload });
  const sliced = events.slice(-Math.max(keep, 20));
  await fs.writeFile(jsonPath, JSON.stringify(sliced, null, 2), "utf-8");
}

/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} jsonPath
 */
async function clearThumbnailEvents(pgPool, jsonPath) {
  if (pgPool) {
    await pgPool.query(`DELETE FROM lecture_thumbnail_events`);
    return;
  }
  await fs.writeFile(jsonPath, "[]", "utf-8");
}

/**
 * 기존 JSON 파일을 한 번 DB로 옮긴 뒤 파일을 백업 이름으로 변경 (PG 사용 시)
 * @param {import("pg").Pool | null} pgPool
 * @param {string} jsonPath
 */
/**
 * @param {import("pg").Pool | null} pgPool
 * @param {string} jsonPath
 * @param {number} [keep]
 */
async function migrateThumbnailEventsFromJson(pgPool, jsonPath, keep) {
  if (!pgPool) return;
  const keepN = Math.max(Number(keep) || 200, 20);
  let rows;
  try {
    const raw = await fs.readFile(jsonPath, "utf-8");
    const parsed = JSON.parse(raw);
    rows = Array.isArray(parsed) ? parsed : [];
  } catch {
    return;
  }
  if (rows.length === 0) return;

  for (const evt of rows) {
    if (!evt || !evt.id || !evt.type) continue;
    try {
      await pgPool.query(
        `INSERT INTO lecture_thumbnail_events (
          id, occurred_at, event_type, lecture_id, lecture_title, reason, force_flag,
          queue_size_after, retry_count, duration_ms, error_text
        ) VALUES ($1::uuid, $2::timestamptz, $3, $4::uuid, $5, $6, $7, $8, $9, $10, $11)
        ON CONFLICT (id) DO NOTHING`,
        [
          evt.id,
          evt.at || new Date().toISOString(),
          evt.type,
          evt.lectureId || null,
          evt.lectureTitle || null,
          evt.reason || null,
          evt.force === true,
          evt.queueSizeAfter != null ? evt.queueSizeAfter : null,
          evt.retryCount != null ? evt.retryCount : null,
          evt.durationMs != null ? evt.durationMs : null,
          evt.error || null,
        ]
      );
    } catch {
      /* ignore row */
    }
  }
  await pruneThumbnailEvents(pgPool, keepN);
  try {
    const fsSync = require("fs");
    if (fsSync.existsSync(jsonPath)) {
      fsSync.renameSync(jsonPath, `${jsonPath}.migrated.bak`);
    }
  } catch (e) {
    console.warn("[thumbnail-events] JSON migration rename failed:", e?.message || e);
  }
}

module.exports = {
  readThumbnailEvents,
  appendThumbnailEvent,
  clearThumbnailEvents,
  migrateThumbnailEventsFromJson,
};
