#!/usr/bin/env node
/**
 * .env.ncue(원본 DB) → .env(대상 DB) 데이터 복사
 *
 * 학습센터(강의 목록): lectures 테이블
 * 선택: lecture_thumbnail_events (썸네일 작업 로그)
 *
 * 사용 전:
 *   1) 대상 DB에 스키마 적용: node scripts/apply-schema.js  (.env 기준)
 *   2) 대상 서버에 강의 파일 동기화(별도): DB만으로는 PPT/슬라이드/썸네일 바이너리가 없음
 *      - resources/lecture/   (업로드 원본 PPT 등)
 *      - uploads/thumbnails/  (썸네일)
 *      - uploads/slides/<강의UUID>/ (슬라이드 이미지)
 *
 * 실행 (저장소 루트에서):
 *   node scripts/migrate-db-ncue-to-env.js
 *   node scripts/migrate-db-ncue-to-env.js --dry-run
 *   node scripts/migrate-db-ncue-to-env.js --with-thumbnail-events
 *
 * 대상에 강의만 원본과 동일하게 맞추고(기존 행 삭제 후 복사):
 *   node scripts/migrate-db-ncue-to-env.js --truncate-target
 *
 * 경로 변경:
 *   SOURCE_ENV=.env.ncue TARGET_ENV=.env node scripts/migrate-db-ncue-to-env.js
 */
require("dotenv").config({ quiet: true });

const fs = require("fs");
const path = require("path");
const dotenv = require("dotenv");
const { Pool } = require("pg");

const ROOT = path.join(__dirname, "..");

const LECTURE_COLUMNS = [
  "id",
  "type",
  "title",
  "description",
  "tags",
  "youtube_url",
  "file_name",
  "original_name",
  "preview_title",
  "slide_count",
  "thumbnail_url",
  "thumbnail_status",
  "thumbnail_retry_count",
  "thumbnail_error",
  "thumbnail_updated_at",
  "created_at",
  "updated_at",
  "list_section",
  "news_url",
];

const THUMB_EVENT_COLUMNS = [
  "id",
  "occurred_at",
  "event_type",
  "lecture_id",
  "lecture_title",
  "reason",
  "force_flag",
  "queue_size_after",
  "retry_count",
  "duration_ms",
  "error_text",
];

function parseArgs(argv) {
  const out = { dryRun: false, withThumbnailEvents: false, truncateTarget: false };
  for (const a of argv) {
    if (a === "--dry-run") out.dryRun = true;
    if (a === "--with-thumbnail-events") out.withThumbnailEvents = true;
    if (a === "--truncate-target") out.truncateTarget = true;
  }
  return out;
}

function loadEnvFile(filePath) {
  const abs = path.isAbsolute(filePath) ? filePath : path.join(ROOT, filePath);
  if (!fs.existsSync(abs)) {
    throw new Error(`환경 파일이 없습니다: ${abs}`);
  }
  return dotenv.parse(fs.readFileSync(abs, "utf8"));
}

function poolConfigFromEnv(e) {
  const missing = ["DB_HOST", "DB_DATABASE", "DB_USERNAME", "DB_PASSWORD"].filter((k) => !e[k]);
  if (missing.length) {
    throw new Error(`DB 설정 누락: ${missing.join(", ")}`);
  }
  return {
    host: e.DB_HOST,
    port: Number(e.DB_PORT || 5432),
    database: e.DB_DATABASE,
    user: e.DB_USERNAME,
    password: e.DB_PASSWORD,
    ssl: e.DB_SSL === "1" ? { rejectUnauthorized: false } : false,
  };
}

function placeholders(n) {
  return Array.from({ length: n }, (_, i) => `$${i + 1}`).join(", ");
}

function buildLectureUpsert() {
  const cols = LECTURE_COLUMNS;
  const ph = placeholders(cols.length);
  const updates = cols
    .filter((c) => c !== "id")
    .map((c) => `${c} = EXCLUDED.${c}`)
    .join(", ");
  return `
    INSERT INTO lectures (${cols.join(", ")})
    VALUES (${ph})
    ON CONFLICT (id) DO UPDATE SET ${updates}
  `;
}

function buildThumbEventUpsert() {
  const cols = THUMB_EVENT_COLUMNS;
  const ph = placeholders(cols.length);
  const updates = cols
    .filter((c) => c !== "id")
    .map((c) => `${c} = EXCLUDED.${c}`)
    .join(", ");
  return `
    INSERT INTO lecture_thumbnail_events (${cols.join(", ")})
    VALUES (${ph})
    ON CONFLICT (id) DO UPDATE SET ${updates}
  `;
}

function rowValues(row, columns) {
  return columns.map((c) => row[c]);
}

async function truncateTargetTables(targetPool, dryRun) {
  if (dryRun) {
    console.log(
      "[truncate] dry-run: lecture_thumbnail_events, lectures TRUNCATE 생략"
    );
    return;
  }
  await targetPool.query("TRUNCATE lecture_thumbnail_events");
  await targetPool.query("TRUNCATE lectures");
  console.log("[truncate] 대상 lecture_thumbnail_events, lectures 비움");
}

async function migrateLectures(sourcePool, targetPool, dryRun) {
  const { rows } = await sourcePool.query(
    `SELECT ${LECTURE_COLUMNS.join(", ")} FROM lectures ORDER BY created_at ASC`
  );
  console.log(`[lectures] 원본 행 수: ${rows.length}`);
  if (dryRun) {
    console.log("[lectures] dry-run: INSERT 생략");
    return rows.length;
  }
  const sql = buildLectureUpsert();
  const client = await targetPool.connect();
  try {
    await client.query("BEGIN");
    let n = 0;
    for (const row of rows) {
      await client.query(sql, rowValues(row, LECTURE_COLUMNS));
      n++;
    }
    await client.query("COMMIT");
    console.log(`[lectures] 대상 DB 반영 완료: ${n}건`);
    return n;
  } catch (e) {
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

async function migrateThumbnailEvents(sourcePool, targetPool, dryRun) {
  const { rows } = await sourcePool.query(
    `SELECT ${THUMB_EVENT_COLUMNS.join(", ")} FROM lecture_thumbnail_events ORDER BY occurred_at ASC`
  );
  console.log(`[lecture_thumbnail_events] 원본 행 수: ${rows.length}`);
  if (dryRun) {
    console.log("[lecture_thumbnail_events] dry-run: INSERT 생략");
    return rows.length;
  }
  const sql = buildThumbEventUpsert();
  const client = await targetPool.connect();
  try {
    await client.query("BEGIN");
    let n = 0;
    for (const row of rows) {
      await client.query(sql, rowValues(row, THUMB_EVENT_COLUMNS));
      n++;
    }
    await client.query("COMMIT");
    console.log(`[lecture_thumbnail_events] 대상 DB 반영 완료: ${n}건`);
    return n;
  } catch (e) {
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

function printFileHint() {
  console.log(`
[파일 동기화 안내] DB만 복사하면 메타데이터만 옮겨집니다. 강의 PPT/슬라이드/썸네일이 비어 있으면
원본 서버(또는 .env.ncue가 가리키는 환경의 디스크)에서 대상(ai.xavis.co.kr) 배포 디렉터리로 복사하세요.

  예시 (원본 호스트에서 대상으로 rsync — 경로는 실제 배포에 맞게 조정):
    rsync -avz ./resources/lecture/   user@ai-host:/path/to/webplatform/resources/lecture/
    rsync -avz ./uploads/thumbnails/  user@ai-host:/path/to/webplatform/uploads/thumbnails/
    rsync -avz ./uploads/slides/      user@ai-host:/path/to/webplatform/uploads/slides/
`);
}

async function main() {
  const args = parseArgs(process.argv.slice(2));
  const sourcePath = process.env.SOURCE_ENV || ".env.ncue";
  const targetPath = process.env.TARGET_ENV || ".env";

  const sourceEnv = loadEnvFile(sourcePath);
  const targetEnv = loadEnvFile(targetPath);

  const srcCfg = poolConfigFromEnv(sourceEnv);
  const tgtCfg = poolConfigFromEnv(targetEnv);

  console.log(`원본: ${sourcePath} → ${srcCfg.host}/${srcCfg.database}`);
  console.log(`대상: ${targetPath} → ${tgtCfg.host}/${tgtCfg.database}`);
  if (args.dryRun) {
    console.log("모드: DRY-RUN (쓰기 없음)\n");
  }

  const sourcePool = new Pool(srcCfg);
  const targetPool = new Pool(tgtCfg);

  try {
    await sourcePool.query("SELECT 1");
    await targetPool.query("SELECT 1");
  } catch (e) {
    console.error("DB 연결 실패:", e.message);
    process.exit(1);
  }

  try {
    if (args.truncateTarget) {
      await truncateTargetTables(targetPool, args.dryRun);
    }
    await migrateLectures(sourcePool, targetPool, args.dryRun);
    if (args.withThumbnailEvents) {
      await migrateThumbnailEvents(sourcePool, targetPool, args.dryRun);
    }
    printFileHint();
    console.log("완료.");
  } catch (e) {
    console.error("마이그레이션 실패:", e.message || e);
    process.exit(1);
  } finally {
    await sourcePool.end();
    await targetPool.end();
  }
}

main();
