import * as admin from 'firebase-admin';

// Firebase Admin SDK 초기화 (GOOGLE_APPLICATION_CREDENTIALS 환경변수 사용)
// Usage (dry-run): GOOGLE_APPLICATION_CREDENTIALS=/home/jay/.config/gcloud/service-accounts/insuwiki-j2h-fa603f4f75f5.json ts-node -P scripts/tsconfig.json scripts/backfill-user-names.ts
// Usage (execute): GOOGLE_APPLICATION_CREDENTIALS=/home/jay/.config/gcloud/service-accounts/insuwiki-j2h-fa603f4f75f5.json ts-node -P scripts/tsconfig.json scripts/backfill-user-names.ts --execute

if (!admin.apps.length) {
  admin.initializeApp();
}

const db = admin.firestore();
const auth = admin.auth();

// ── 상수 ─────────────────────────────────────────────────────────────────────

const BATCH_SIZE = 10; // Firebase Auth API rate limit 방지

// ── 타입 정의 ─────────────────────────────────────────────────────────────────

interface BackfillResult {
  total: number;
  updated: number;
  skipped: number;
  errors: number;
}

type UserStatus =
  | 'updated_from_firestore'
  | 'updated_from_auth'
  | 'skipped_already_has_name'
  | 'skipped_no_name_found'
  | `error: ${string}`;

interface UserResult {
  uid: string;
  name?: string;
  status: UserStatus;
}

// ── 핵심 함수 ─────────────────────────────────────────────────────────────────

async function resolveNameForUser(
  uid: string,
  data: FirebaseFirestore.DocumentData,
): Promise<{ name: string; source: 'firestore_displayName' | 'auth' } | null> {
  // 1. Firestore displayName 필드 확인
  const displayName = data.displayName;
  if (typeof displayName === 'string' && displayName.trim().length > 0) {
    return { name: displayName.trim(), source: 'firestore_displayName' };
  }

  // 2. Firebase Auth에서 displayName 조회
  try {
    const userRecord = await auth.getUser(uid);
    if (userRecord.displayName && userRecord.displayName.trim().length > 0) {
      return { name: userRecord.displayName.trim(), source: 'auth' };
    }
  } catch (err: any) {
    if (err.code !== 'auth/user-not-found') {
      throw err;
    }
    // Auth에 유저가 없는 경우는 무시하고 null 반환
  }

  return null;
}

async function processBatch(
  docs: FirebaseFirestore.QueryDocumentSnapshot[],
  dryRun: boolean,
): Promise<{ updated: number; skipped: number; errors: number; details: UserResult[] }> {
  let updated = 0;
  let skipped = 0;
  let errors = 0;
  const details: UserResult[] = [];

  await Promise.all(
    docs.map(async (userDoc) => {
      const uid = userDoc.id;
      const data = userDoc.data();

      // name 필드가 이미 존재하고 비어 있지 않은 경우 스킵
      if (typeof data.name === 'string' && data.name.trim().length > 0) {
        skipped++;
        details.push({ uid, name: data.name, status: 'skipped_already_has_name' });
        console.log(`  [SKIP] uid=${uid} → name 필드 이미 존재: "${data.name}"`);
        return;
      }

      try {
        const resolved = await resolveNameForUser(uid, data);

        if (!resolved) {
          skipped++;
          details.push({ uid, status: 'skipped_no_name_found' });
          console.warn(`  [WARN] uid=${uid} → displayName을 찾을 수 없음, 스킵`);
          return;
        }

        const { name, source } = resolved;
        const statusLabel: UserStatus =
          source === 'firestore_displayName' ? 'updated_from_firestore' : 'updated_from_auth';

        if (dryRun) {
          console.log(`  [DRY-RUN] uid=${uid} → name="${name}" (출처: ${source})`);
        } else {
          await userDoc.ref.update({ name });
          console.log(`  [UPDATE] uid=${uid} → name="${name}" (출처: ${source})`);
        }

        updated++;
        details.push({ uid, name, status: statusLabel });
      } catch (err: any) {
        errors++;
        const msg = err.message ?? String(err);
        details.push({ uid, status: `error: ${msg}` });
        console.error(`  [ERROR] uid=${uid} → ${msg}`);
      }
    }),
  );

  return { updated, skipped, errors, details };
}

async function backfillUserNames(dryRun: boolean): Promise<BackfillResult> {
  console.log(`[INFO] 유저 name 백필 시작 (dryRun=${dryRun})`);

  const result: BackfillResult = {
    total: 0,
    updated: 0,
    skipped: 0,
    errors: 0,
  };

  const snapshot = await db.collection('users').get();
  result.total = snapshot.docs.length;

  console.log(`[INFO] 전체 유저 수: ${result.total}\n`);

  const totalBatches = Math.ceil(snapshot.docs.length / BATCH_SIZE);

  for (let i = 0; i < snapshot.docs.length; i += BATCH_SIZE) {
    const batchDocs = snapshot.docs.slice(i, i + BATCH_SIZE);
    const batchNum = Math.floor(i / BATCH_SIZE) + 1;

    console.log(`[INFO] 배치 ${batchNum}/${totalBatches} 처리 중 (${batchDocs.length}건)...`);

    const batchResult = await processBatch(batchDocs, dryRun);

    result.updated += batchResult.updated;
    result.skipped += batchResult.skipped;
    result.errors += batchResult.errors;
  }

  return result;
}

// ── 엔트리포인트 ─────────────────────────────────────────────────────────────

async function main() {
  const dryRun = !process.argv.includes('--execute');

  if (dryRun) {
    console.log('[DRY-RUN] --execute 플래그 없이 실행 중입니다. Firestore 실제 쓰기는 발생하지 않습니다.');
    console.log(
      '[DRY-RUN] 실제 실행: GOOGLE_APPLICATION_CREDENTIALS=... ts-node -P scripts/tsconfig.json scripts/backfill-user-names.ts --execute\n',
    );
  } else {
    console.log('[EXECUTE] Firestore users 컬렉션에 실제 name 필드 쓰기가 실행됩니다.\n');
  }

  const result = await backfillUserNames(dryRun);

  console.log('\n========== 유저 name 백필 완료 ==========');
  console.log(`전체 유저 수  : ${result.total}`);
  console.log(`업데이트 수   : ${result.updated}${dryRun ? ' (dry-run, 실제 쓰기 없음)' : ''}`);
  console.log(`스킵 수       : ${result.skipped} (이미 name 있음 또는 displayName 없음)`);
  console.log(`오류 수       : ${result.errors}`);
  console.log('=========================================');

  if (result.errors > 0) {
    process.exit(1);
  }
}

main().catch((err) => {
  console.error('[FATAL]', err);
  process.exit(1);
});
