/**
 * 깨진 밑줄 태그 복구 스크립트
 *
 * 에디터 버그로 인해 `</u>` 대신 `</u` (닫힘 `>` 누락) 형태로 저장된
 * 문서의 content 필드를 검색하고 수정합니다.
 *
 * 대상 컬렉션: documents, dailyNotes, drafts
 *
 * 사용법:
 *   npx ts-node scripts/fix-broken-underline-tags.ts            # dry-run (기본값)
 *   npx ts-node scripts/fix-broken-underline-tags.ts --dry-run  # dry-run (명시)
 *   npx ts-node scripts/fix-broken-underline-tags.ts --fix      # 실제 수정 적용
 *
 * task-1349.1: 깨진 밑줄 태그 복구
 * 생성일: 2026-04-02
 */

import * as admin from 'firebase-admin';
import * as path from 'path';
import * as fs from 'fs';

// ============================================================
// Firebase Admin 초기화
// ============================================================
if (!admin.apps.length) {
    const localKeyPath = path.resolve(__dirname, '../temp.j2h/insuwiki-j2h-902be7d0b6f5.json');
    if (fs.existsSync(localKeyPath)) {
        try {
            const serviceAccount = JSON.parse(fs.readFileSync(localKeyPath, 'utf8'));
            admin.initializeApp({
                credential: admin.credential.cert(serviceAccount),
                projectId: 'insuwiki-j2h',
            });
        } catch {
            admin.initializeApp();
            console.warn('서비스 계정 키 파싱 실패 — 기본 인증 사용');
        }
    } else {
        admin.initializeApp();
        console.log('서비스 계정 키 없음 — 기본 인증 사용 (gcloud auth)');
    }
}

const db = admin.firestore();

// ============================================================
// 상수
// ============================================================
const BATCH_SIZE = 500;

// content 필드를 가진 대상 컬렉션 목록
const TARGET_COLLECTIONS = [
    'documents',
    'dailyNotes',
    'drafts',
] as const;

// ============================================================
// 깨진 태그 수정 함수
// ============================================================

/**
 * content 문자열에서 `</u` 패턴 여부를 확인합니다.
 *
 * 패턴:
 *   1. `</u` 뒤에 `>` 이외의 문자(공백, 줄바꿈, 알파벳 등)가 오는 경우
 *   2. `</u` 가 문자열 끝에 오는 경우
 */
function hasBrokenUnderlineTag(content: string): boolean {
    // `</u` 다음이 `>` 가 아니거나 문자열 끝인 경우
    return /(<\/u)(?!>)/m.test(content);
}

/**
 * content 문자열의 깨진 `</u` 패턴을 `</u>`로 수정합니다.
 *
 * - `</u` + (공백/줄바꿈/알파벳 등 `>` 가 아닌 문자) → `</u>` + 나머지
 * - `</u` + 문자열 끝 → `</u>`
 */
function fixBrokenUnderlineTags(content: string): string {
    // `</u` 뒤에 `>` 가 아닌 문자가 오는 경우: `>` 삽입 후 나머지 유지
    // `</u` 가 문자열 끝인 경우도 동일하게 처리 (캡처 그룹 없으면 빈 문자열)
    return content.replace(/(<\/u)(?!>)(.|$)/gm, (_, tag, after) => {
        return `${tag}>${after}`;
    });
}

// ============================================================
// 간단한 diff 출력 (변경 전/후 비교)
// ============================================================
function printDiff(docId: string, collection: string, before: string, after: string): void {
    const beforeLines = before.split('\n');
    const afterLines = after.split('\n');

    console.log(`\n  문서: ${collection}/${docId}`);

    // 변경된 줄만 출력 (최대 5줄)
    let diffCount = 0;
    const maxDiffLines = 5;

    for (let i = 0; i < Math.max(beforeLines.length, afterLines.length); i++) {
        const b = beforeLines[i] ?? '';
        const a = afterLines[i] ?? '';
        if (b !== a && diffCount < maxDiffLines) {
            console.log(`    L${i + 1} - ${JSON.stringify(b)}`);
            console.log(`    L${i + 1} + ${JSON.stringify(a)}`);
            diffCount++;
        }
    }

    if (diffCount === 0) {
        // 줄 단위로는 같지만 문자열이 다른 경우 (개행 없이 변경)
        console.log(`    (인라인 수정, before/after 문자열 길이: ${before.length} → ${after.length})`);
    }
}

// ============================================================
// 단일 컬렉션 처리
// ============================================================
interface CollectionResult {
    collection: string;
    totalScanned: number;
    brokenCount: number;
    fixedCount: number;
}

async function processCollection(
    collection: string,
    dryRun: boolean
): Promise<CollectionResult> {
    const result: CollectionResult = {
        collection,
        totalScanned: 0,
        brokenCount: 0,
        fixedCount: 0,
    };

    const snapshot = await db.collection(collection).get();
    result.totalScanned = snapshot.size;

    if (result.totalScanned === 0) {
        console.log(`  [${collection}] 문서 없음`);
        return result;
    }

    // 깨진 태그가 포함된 문서 필터링
    type DocEntry = { doc: admin.firestore.QueryDocumentSnapshot; fixed: string };
    const docsToFix: DocEntry[] = [];

    for (const doc of snapshot.docs) {
        const data = doc.data();
        const content: unknown = data['content'];
        if (typeof content === 'string' && hasBrokenUnderlineTag(content)) {
            const fixed = fixBrokenUnderlineTags(content);
            docsToFix.push({ doc, fixed });
        }
    }

    result.brokenCount = docsToFix.length;

    if (docsToFix.length === 0) {
        console.log(`  [${collection}] 스캔: ${result.totalScanned}개 — 깨진 태그 없음`);
        return result;
    }

    console.log(
        `  [${collection}] 스캔: ${result.totalScanned}개 — 깨진 태그 발견: ${result.brokenCount}개`
    );

    // diff 출력 (모든 대상 문서)
    for (const { doc, fixed } of docsToFix) {
        const before = doc.data()['content'] as string;
        printDiff(doc.id, collection, before, fixed);
    }

    if (dryRun) {
        return result;
    }

    // 실제 수정: 배치 처리
    let batchNumber = 0;

    for (let i = 0; i < docsToFix.length; i += BATCH_SIZE) {
        const chunk = docsToFix.slice(i, i + BATCH_SIZE);
        batchNumber++;

        const batch = db.batch();

        chunk.forEach(({ doc, fixed }) => {
            const originalContent = doc.data()['content'] as string;
            batch.update(doc.ref, {
                content: fixed,
                _content_backup: originalContent,   // 원본 백업
                updatedAt: admin.firestore.FieldValue.serverTimestamp(),
            });
        });

        await batch.commit();
        result.fixedCount += chunk.length;
        console.log(
            `  [${collection}] 배치 ${batchNumber}: ${chunk.length}개 수정 완료`
        );
    }

    return result;
}

// ============================================================
// 메인
// ============================================================
async function main(dryRun: boolean): Promise<void> {
    const mode = dryRun ? 'DRY-RUN' : '실제 수정';

    console.log(`\n${'='.repeat(60)}`);
    console.log(`깨진 밑줄 태그 복구 스크립트 — ${mode} 모드`);
    console.log(`대상 패턴: </u (닫힘 > 누락)`);
    console.log(`대상 컬렉션: ${TARGET_COLLECTIONS.join(', ')}`);
    console.log(`${'='.repeat(60)}\n`);

    const results: CollectionResult[] = [];

    for (const collection of TARGET_COLLECTIONS) {
        const result = await processCollection(collection, dryRun);
        results.push(result);
    }

    // 최종 요약
    const totalScanned = results.reduce((s, r) => s + r.totalScanned, 0);
    const totalBroken = results.reduce((s, r) => s + r.brokenCount, 0);
    const totalFixed = results.reduce((s, r) => s + r.fixedCount, 0);

    console.log(`\n${'='.repeat(60)}`);
    console.log('요약');
    console.log(`${'='.repeat(60)}`);
    console.log(`총 스캔 문서: ${totalScanned}개`);
    console.log(`깨진 태그 발견: ${totalBroken}개`);

    if (dryRun) {
        console.log(`수정 예정: ${totalBroken}개 (DRY-RUN — 실제 변경 없음)`);
        console.log('\n실제 수정을 적용하려면:');
        console.log('  npx ts-node scripts/fix-broken-underline-tags.ts --fix');
    } else {
        console.log(`수정 완료: ${totalFixed}개`);
        console.log('  * 원본 content는 _content_backup 필드에 백업되었습니다.');
    }

    console.log(`${'='.repeat(60)}\n`);
}

// ============================================================
// 실행 진입점
// ============================================================
const args = process.argv.slice(2);
const isFix = args.includes('--fix');
const isDryRun = !isFix; // --fix 가 없으면 dry-run이 기본값

main(isDryRun)
    .then(() => process.exit(0))
    .catch(err => {
        console.error('\n스크립트 실패:', err);
        process.exit(1);
    });
