/**
 * insurance_terms 시드 스크립트
 *
 * /home/jay/workspace/memory/research/insurance-terms-dictionary.md 파일을 파싱하여
 * Firestore insurance_terms 컬렉션에 시딩합니다.
 *
 * 사용법:
 *   npx ts-node scripts/seedInsuranceTerms.ts           # 실제 실행
 *   npx ts-node scripts/seedInsuranceTerms.ts --dry-run  # dry-run 모드
 *
 * MT-6: insurance_terms 시드 스크립트
 * 생성일: 2026-03-23
 */

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();
    }
}

const db = admin.firestore();

// ============================================================
// 타입 정의
// ============================================================
interface InsuranceTerm {
    term: string;
    commonAliases: string[];
    category: string;
    subcategory: string;
    section: string;
    definition: string;
    verified: boolean;
    createdAt: admin.firestore.Timestamp;
}

interface ParseStats {
    total: number;
    byCategory: Record<string, number>;
    skippedLines: number;
}

// ============================================================
// 마크다운 파싱 유틸
// ============================================================

/**
 * 괄호 안 내용이 ICD 코드나 범위 설명인지 판단
 * 예: "가장 좁은 범위, I60-I62", "I60-I62", "GA, General Average"
 * 이런 경우 alias가 아닌 설명으로 간주하여 제외
 */
function isDescriptionNotAlias(content: string): boolean {
    // ICD 코드 패턴 포함 (예: I60-I62, I21-I22)
    if (/[A-Z]\d{2}-[A-Z]\d{2}/.test(content)) return true;
    // "가장 좁은 범위", "중간 범위", "가장 넓은 범위" 등 범위 설명
    if (/(가장\s*(좁은|넓은)|중간)\s*범위/.test(content)) return true;
    // 단독 영문 약어 2글자 이상 + 영문만으로 구성된 경우 (단, 한글 섞인 alias는 허용)
    // 예: "TLO", "GA, General Average", "W.A., With Average" — 단, "VUL", "CI" 같은 것은 alias로 허용
    // 쉼표 포함 + 영문만인 경우 제외
    if (/^[A-Za-z\s,./]+$/.test(content) && content.includes(',')) return true;
    return false;
}

/**
 * 용어 라인을 파싱하여 term과 commonAliases 추출
 * 형식: "용어명" 또는 "용어명 (alias1, alias2)"
 */
function parseTermLine(line: string): { term: string; commonAliases: string[] } | null {
    const trimmed = line.trim();
    if (!trimmed) return null;

    // 괄호가 있는 경우: "용어명 (alias1, alias2, ...)"
    const parenMatch = trimmed.match(/^(.+?)\s*\((.+)\)\s*$/);
    if (parenMatch) {
        const term = parenMatch[1].trim();
        const parenContent = parenMatch[2].trim();

        // 괄호 안이 설명(ICD 코드, 범위 설명 등)인 경우 alias 없이 term만 반환
        if (isDescriptionNotAlias(parenContent)) {
            return { term, commonAliases: [] };
        }

        // 쉼표로 구분된 alias 목록 파싱
        const aliases = parenContent
            .split(',')
            .map(a => a.trim())
            .filter(a => a.length > 0);

        return { term, commonAliases: aliases };
    }

    // 괄호 없는 경우: 용어명만
    return { term: trimmed, commonAliases: [] };
}

/**
 * 마크다운 파일을 파싱하여 InsuranceTerm 배열 반환
 */
function parseDictionary(filePath: string): { terms: InsuranceTerm[]; stats: ParseStats } {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');

    const terms: InsuranceTerm[] = [];
    const stats: ParseStats = {
        total: 0,
        byCategory: {},
        skippedLines: 0,
    };

    const now = admin.firestore.Timestamp.now();

    let currentCategory = '';
    let currentSubcategory = '';
    let currentSection = '';
    let reachedStatisticsSection = false;

    for (let i = 0; i < lines.length; i++) {
        const rawLine = lines[i];
        const line = rawLine.trim();

        // 통계 섹션 이후는 스킵 (제이회장님 검수 요청사항 포함)
        if (/^##\s+통계/.test(line) || /^##\s+제이회장님/.test(line)) {
            reachedStatisticsSection = true;
        }
        if (reachedStatisticsSection) {
            stats.skippedLines++;
            continue;
        }

        // 빈 줄 스킵
        if (!line) {
            stats.skippedLines++;
            continue;
        }

        // 수평선 스킵
        if (line === '---') {
            stats.skippedLines++;
            continue;
        }

        // 인용문 스킵 (> 로 시작)
        if (line.startsWith('> ')) {
            stats.skippedLines++;
            continue;
        }

        // 메타데이터 라인 스킵 (**로 시작하는 굵은 텍스트 라인)
        if (line.startsWith('**')) {
            stats.skippedLines++;
            continue;
        }

        // ## 대분류 헤더 파싱 (### 이상은 별도 처리하므로 정확히 ## 만 매칭)
        if (/^##\s+/.test(line) && !/^###/.test(line)) {
            const match = line.match(/^##\s+(.+)$/);
            if (match) {
                currentCategory = match[1].trim();
                currentSubcategory = '';
                currentSection = '';
            }
            stats.skippedLines++;
            continue;
        }

        // ### 중분류 헤더 파싱 (#### 이상은 별도 처리)
        if (/^###\s+/.test(line) && !/^####/.test(line)) {
            const match = line.match(/^###\s+(.+)$/);
            if (match) {
                currentSubcategory = match[1].trim();
                currentSection = '';
            }
            stats.skippedLines++;
            continue;
        }

        // #### 소분류 헤더 파싱
        if (/^####\s+/.test(line)) {
            const match = line.match(/^####\s+(.+)$/);
            if (match) {
                currentSection = match[1].trim();
            }
            stats.skippedLines++;
            continue;
        }

        // ##### 이상 헤더 스킵 (예외적 구조 방어)
        if (/^#/.test(line)) {
            stats.skippedLines++;
            continue;
        }

        // 용어 라인 파싱
        const parsed = parseTermLine(line);
        if (!parsed) {
            stats.skippedLines++;
            continue;
        }

        // 카테고리 없는 용어는 스킵 (파일 상단 메타데이터 라인 등)
        if (!currentCategory) {
            stats.skippedLines++;
            continue;
        }

        const termDoc: InsuranceTerm = {
            term: parsed.term,
            commonAliases: parsed.commonAliases,
            category: currentCategory,
            subcategory: currentSubcategory,
            section: currentSection,
            definition: '',
            verified: false,
            createdAt: now,
        };

        terms.push(termDoc);
        stats.total++;

        // 카테고리별 통계
        if (!stats.byCategory[currentCategory]) {
            stats.byCategory[currentCategory] = 0;
        }
        stats.byCategory[currentCategory]++;
    }

    return { terms, stats };
}

/**
 * term을 안전한 Firestore 문서 ID로 변환
 * Firestore는 유니코드(한글) 허용, 단 '/', '.', '__'는 피함
 */
function termToDocId(term: string): string {
    return term
        .replace(/\//g, '_')      // 슬래시 제거 (예: "N번받는암진단비")
        .replace(/\./g, '_')      // 점 제거
        .replace(/\s+/g, '_')     // 공백 → 언더스코어
        .replace(/__+/g, '_')     // 연속 언더스코어 정리
        .replace(/^_|_$/g, '');   // 앞뒤 언더스코어 제거
}

// ============================================================
// 메인 로직
// ============================================================
async function seedInsuranceTerms(dryRun: boolean): Promise<void> {
    console.log(`\n${'='.repeat(60)}`);
    console.log(`insurance_terms 시드 시작 (${dryRun ? 'DRY-RUN 모드' : '실제 실행 모드'})`);
    console.log(`${'='.repeat(60)}\n`);

    // 사전 파일 경로
    const dictPath = '/home/jay/workspace/memory/research/insurance-terms-dictionary.md';

    if (!fs.existsSync(dictPath)) {
        throw new Error(`사전 파일을 찾을 수 없습니다: ${dictPath}`);
    }

    // 파싱
    console.log(`📖 사전 파일 파싱 중: ${dictPath}`);
    const { terms, stats } = parseDictionary(dictPath);

    console.log(`\n📊 파싱 결과:`);
    console.log(`  - 총 용어 수: ${stats.total}개`);
    console.log(`  - 스킵된 라인 수: ${stats.skippedLines}개`);
    console.log(`\n📂 카테고리별 용어 수:`);
    Object.entries(stats.byCategory).forEach(([cat, count]) => {
        console.log(`  [${count.toString().padStart(4)}] ${cat}`);
    });

    if (terms.length === 0) {
        console.warn('\n⚠️ 파싱된 용어가 없습니다. 파일 형식을 확인하세요.');
        return;
    }

    // dry-run 모드: 실제 저장 없이 결과만 출력
    if (dryRun) {
        console.log('\n[DRY-RUN] 처음 5개 용어 미리보기:');
        terms.slice(0, 5).forEach((t, idx) => {
            console.log(`\n  [${idx + 1}] term: "${t.term}"`);
            console.log(`       aliases: [${t.commonAliases.map(a => `"${a}"`).join(', ')}]`);
            console.log(`       category: "${t.category}"`);
            console.log(`       subcategory: "${t.subcategory}"`);
            console.log(`       section: "${t.section}"`);
            console.log(`       docId: "${termToDocId(t.term)}"`);
        });
        console.log(`\n[DRY-RUN] 총 ${terms.length}개 용어가 insurance_terms 컬렉션에 저장될 예정입니다.`);
        console.log('[DRY-RUN] 실제 실행하려면 --dry-run 플래그 없이 실행하세요.');
        console.log(`\n${'='.repeat(60)}\n`);
        return;
    }

    // Firestore batch write (500건 단위)
    const BATCH_SIZE = 500;
    const totalBatches = Math.ceil(terms.length / BATCH_SIZE);
    let successCount = 0;
    let failCount = 0;

    console.log(`\n🔥 Firestore 배치 쓰기 시작 (총 ${totalBatches}개 배치, 배치당 최대 ${BATCH_SIZE}건)`);

    for (let batchIdx = 0; batchIdx < totalBatches; batchIdx++) {
        const start = batchIdx * BATCH_SIZE;
        const end = Math.min(start + BATCH_SIZE, terms.length);
        const batchTerms = terms.slice(start, end);

        console.log(`\n  배치 ${batchIdx + 1}/${totalBatches} 처리 중... (${start + 1}~${end}번 용어)`);

        try {
            const batch = db.batch();

            batchTerms.forEach(termDoc => {
                const docId = termToDocId(termDoc.term);
                const ref = db.collection('insurance_terms').doc(docId);
                // upsert: term 기준으로 덮어쓰기 (merge: false = 전체 교체)
                batch.set(ref, termDoc);
            });

            await batch.commit();
            successCount += batchTerms.length;
            console.log(`  ✅ 배치 ${batchIdx + 1} 완료 (${batchTerms.length}건 저장)`);
        } catch (err) {
            failCount += batchTerms.length;
            console.error(`  ❌ 배치 ${batchIdx + 1} 실패:`, err);
        }
    }

    // 최종 결과
    console.log(`\n${'='.repeat(60)}`);
    console.log(`📋 최종 결과:`);
    console.log(`  - 총 파싱: ${terms.length}개`);
    console.log(`  - 성공:    ${successCount}개`);
    console.log(`  - 실패:    ${failCount}개`);

    if (failCount === 0) {
        console.log(`\n✅ insurance_terms 시드 완료! Firestore 컬렉션을 확인하세요.`);
    } else {
        console.warn(`\n⚠️ 일부 배치가 실패했습니다. 위 로그를 확인하세요.`);
    }

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

// ============================================================
// 실행
// ============================================================
const isDryRun = process.argv.includes('--dry-run');

seedInsuranceTerms(isDryRun)
    .then(() => process.exit(0))
    .catch(err => {
        console.error('\n❌ 시드 실패:', err);
        process.exit(1);
    });
