/**
 * youtube-upload-summary.ts
 *
 * 아누(봇)가 팀원(Claude Sonnet)에게 요약을 위임한 후,
 * 완성된 요약을 Google Drive에 업로드하고 Firestore를 업데이트하는 유틸리티 스크립트.
 *
 * 사용법:
 *   # 파일에서 요약 읽기
 *   npx ts-node scripts/youtube-upload-summary.ts --videoId <videoId> --summary-file <path>
 *
 *   # stdin에서 요약 읽기 (pipe)
 *   cat summary.md | npx ts-node scripts/youtube-upload-summary.ts --videoId <videoId> --stdin
 */

import * as admin from 'firebase-admin';
import * as fs from 'fs';
import * as path from 'path';
import * as dotenv from 'dotenv';
import { google } from 'googleapis';
import { GoogleGenerativeAI } from '@google/generative-ai';

// ── 환경변수 로드 ────────────────────────────────────────────────────────────
dotenv.config({ path: path.resolve(__dirname, '../.env.local') });
dotenv.config({ path: path.resolve(__dirname, '../.env') });

// ── Firebase Admin 초기화 ────────────────────────────────────────────────────
if (!admin.apps.length) {
    const localKeyPath = path.resolve(__dirname, '../temp.j2h/insuwiki-j2h-902be7d0b6f5.json');

    let credential: admin.credential.Credential | undefined;

    if (fs.existsSync(localKeyPath)) {
        try {
            credential = admin.credential.cert(
                JSON.parse(fs.readFileSync(localKeyPath, 'utf8'))
            );
            console.log('[Firebase] 로컬 서비스 계정 키로 초기화');
        } catch (e) {
            console.warn('[Firebase] 로컬 키 파싱 실패, 다른 방법으로 시도합니다');
        }
    }

    if (!credential) {
        const serviceAccountKey = process.env.GOOGLE_SERVICE_ACCOUNT_KEY;
        if (serviceAccountKey) {
            try {
                credential = admin.credential.cert(JSON.parse(serviceAccountKey));
                console.log('[Firebase] GOOGLE_SERVICE_ACCOUNT_KEY 환경변수로 초기화');
            } catch (e) {
                console.warn('[Firebase] GOOGLE_SERVICE_ACCOUNT_KEY 파싱 실패');
            }
        }
    }

    const projectId =
        process.env.FIREBASE_PROJECT_ID ||
        process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID ||
        'insuwiki-j2h';

    try {
        if (credential) {
            admin.initializeApp({ credential, projectId });
        } else {
            console.warn('[Firebase] 서비스 계정 키 없음 → ADC(Application Default Credentials) 시도');
            admin.initializeApp({
                credential: admin.credential.applicationDefault(),
                projectId,
            });
        }
    } catch (err) {
        console.error('[Firebase] 초기화 실패:', err);
        console.error('  GOOGLE_SERVICE_ACCOUNT_KEY 또는 temp.j2h/ 키 파일을 확인하세요.');
        process.exit(1);
    }
}

const db = admin.firestore();

// ── 타입 정의 ─────────────────────────────────────────────────────────────────
interface YoutubeKnowledgeDoc {
    videoId: string;
    channelId?: string;
    channelName: string;
    title: string;
    publishedAt?: admin.firestore.Timestamp;
    chunkText?: string;
    embedding?: number[];
    driveUrl?: string;
    driveTranscriptUrl?: string;
    hasTranscript?: boolean;
    transcriptionSource?: string;
    summaryStatus?: string;
    sourceType?: string;
    relatedCompanyIds?: string[];
    relatedProductIds?: string[];
    conflictsWithPolicy?: boolean;
    createdAt?: admin.firestore.Timestamp;
}

// ── Google Drive 유틸 ────────────────────────────────────────────────────────
async function findOrCreateFolder(
    drive: ReturnType<typeof google.drive>,
    parentId: string,
    folderName: string
): Promise<string> {
    const res = await drive.files.list({
        q: `'${parentId}' in parents and name='${folderName}' and mimeType='application/vnd.google-apps.folder' and trashed=false`,
        fields: 'files(id)',
        pageSize: 1,
    });

    if (res.data.files && res.data.files.length > 0) {
        return res.data.files[0].id!;
    }

    const created = await drive.files.create({
        requestBody: {
            name: folderName,
            mimeType: 'application/vnd.google-apps.folder',
            parents: [parentId],
        },
        fields: 'id',
    });
    return created.data.id!;
}

async function uploadMarkdownToDrive(
    drive: ReturnType<typeof google.drive>,
    content: string,
    channelName: string,
    videoTitle: string,
    videoDate: string,
    rootFolderId: string,
    suffix: string = '_요약'
): Promise<string> {
    // 04_유튜브요약/{channelName}/ 폴더 확인/생성
    const youtubeFolder = await findOrCreateFolder(drive, rootFolderId, '04_유튜브요약');
    const channelFolder = await findOrCreateFolder(drive, youtubeFolder, channelName);

    const safeTitle = videoTitle.replace(/[/\\:*?"<>|]/g, '_').slice(0, 50);
    const fileName = `${videoDate}_${safeTitle}${suffix}.md`;

    // 중복 파일 방지: 동일 파일명 존재 시 기존 파일 URL 반환
    const existingFile = await drive.files.list({
        q: `'${channelFolder}' in parents and name='${fileName}' and trashed=false`,
        fields: 'files(id, webViewLink)',
        pageSize: 1,
    });
    if (existingFile.data.files && existingFile.data.files.length > 0) {
        console.log(`[Drive] 중복 파일 발견, 기존 URL 반환: ${fileName}`);
        const existing = existingFile.data.files[0];
        return existing.webViewLink || existing.id || '';
    }

    const res = await drive.files.create({
        requestBody: {
            name: fileName,
            mimeType: 'text/plain',
            parents: [channelFolder],
        },
        media: {
            mimeType: 'text/plain',
            body: content,
        },
        fields: 'id, webViewLink',
    });

    console.log(`[Drive] 업로드 완료: ${fileName}`);
    return res.data.webViewLink || res.data.id || '';
}

// ── 마크다운 콘텐츠 생성 (buildMarkdownContent 동일 형식) ───────────────────
// crawlYoutubeChannels.ts의 buildMarkdownContent와 동일한 구조이되,
// 마지막 줄만 "Claude Sonnet" 표기로 변경
function buildMarkdownContent(
    summary: string,
    videoTitle: string,
    channelName: string,
    videoId: string,
    publishedAt: string,
    duration: string,
    hasTranscript: boolean
): string {
    return `# ${videoTitle}

- **채널**: ${channelName}
- **업로드일**: ${publishedAt.slice(0, 10)}
- **영상 길이**: ${duration}
- **원본 URL**: https://www.youtube.com/watch?v=${videoId}
- **처리일**: ${new Date().toISOString().slice(0, 19)}+09:00
- **자막 기반**: ${hasTranscript ? '✅ 자막 추출' : '⚠️ 설명+제목만 (자막 없음)'}

---

${summary}

---
*자동 생성 | Claude Sonnet | 원본 내용과 다를 수 있으니 약관 원문을 우선하세요*
`;
}

// ── stdin에서 텍스트 읽기 ────────────────────────────────────────────────────
async function readFromStdin(): Promise<string> {
    return new Promise((resolve, reject) => {
        let data = '';
        process.stdin.setEncoding('utf8');
        process.stdin.on('data', (chunk) => {
            data += chunk;
        });
        process.stdin.on('end', () => {
            resolve(data);
        });
        process.stdin.on('error', reject);
    });
}

// ── 텍스트를 청크 단위로 분할 ────────────────────────────────────────────────
function chunkText(text: string, chunkSize = 500, overlap = 50): string[] {
    const chunks: string[] = [];
    let start = 0;
    while (start < text.length) {
        const end = Math.min(start + chunkSize, text.length);
        chunks.push(text.slice(start, end));
        if (end === text.length) break;
        start += chunkSize - overlap;
    }
    return chunks;
}

// ── 메인 로직 ────────────────────────────────────────────────────────────────
async function main(): Promise<void> {
    const args = process.argv.slice(2);

    // ── 인수 파싱 ──────────────────────────────────────────────────────────
    const videoIdIdx = args.indexOf('--videoId');
    const summaryFileIdx = args.indexOf('--summary-file');
    const useStdin = args.includes('--stdin');

    if (videoIdIdx === -1 || !args[videoIdIdx + 1]) {
        console.error('[오류] --videoId <videoId> 옵션이 필요합니다.');
        console.error('사용법:');
        console.error('  npx ts-node scripts/youtube-upload-summary.ts --videoId <videoId> --summary-file <path>');
        console.error('  cat summary.md | npx ts-node scripts/youtube-upload-summary.ts --videoId <videoId> --stdin');
        process.exit(1);
    }

    const videoId = args[videoIdIdx + 1];

    if (!useStdin && summaryFileIdx === -1) {
        console.error('[오류] --summary-file <path> 또는 --stdin 옵션 중 하나가 필요합니다.');
        process.exit(1);
    }

    if (useStdin && summaryFileIdx !== -1) {
        console.error('[오류] --summary-file 과 --stdin 은 동시에 사용할 수 없습니다.');
        process.exit(1);
    }

    console.log(`\n=== youtube-upload-summary 시작 ===`);
    console.log(`[1/9] 대상 videoId: ${videoId}`);

    // ── 1. Firestore youtube_knowledge 문서 조회 ───────────────────────────
    console.log(`[2/9] Firestore youtube_knowledge 조회 중...`);

    let knowledgeDocRef: admin.firestore.DocumentReference | null = null;
    let knowledgeData: YoutubeKnowledgeDoc | null = null;

    try {
        const snapshot = await db
            .collection('youtube_knowledge')
            .where('videoId', '==', videoId)
            .limit(1)
            .get();

        if (snapshot.empty) {
            console.error(`[오류] youtube_knowledge에 videoId="${videoId}" 문서가 없습니다.`);
            console.error('  crawlYoutubeChannels가 먼저 실행되어 문서가 생성되어 있어야 합니다.');
            process.exit(1);
        }

        knowledgeDocRef = snapshot.docs[0].ref;
        knowledgeData = snapshot.docs[0].data() as YoutubeKnowledgeDoc;
        console.log(`[2/9] 문서 조회 완료: ${knowledgeData.title} (채널: ${knowledgeData.channelName})`);
    } catch (err) {
        console.error('[오류] Firestore 조회 실패:', err);
        process.exit(1);
    }

    // ── 2. 요약 텍스트 읽기 ───────────────────────────────────────────────
    console.log(`[3/9] 요약 텍스트 읽기 (${useStdin ? 'stdin' : '파일'})...`);

    let summaryText = '';

    if (useStdin) {
        try {
            summaryText = await readFromStdin();
            summaryText = summaryText.trim();
        } catch (err) {
            console.error('[오류] stdin 읽기 실패:', err);
            process.exit(1);
        }
    } else {
        const summaryFilePath = args[summaryFileIdx + 1];
        if (!summaryFilePath) {
            console.error('[오류] --summary-file 에 파일 경로를 지정하세요.');
            process.exit(1);
        }

        if (!fs.existsSync(summaryFilePath)) {
            console.error(`[오류] 요약 파일을 찾을 수 없습니다: ${summaryFilePath}`);
            process.exit(1);
        }

        try {
            summaryText = fs.readFileSync(summaryFilePath, 'utf-8').trim();
        } catch (err) {
            console.error(`[오류] 요약 파일 읽기 실패 (${summaryFilePath}):`, err);
            process.exit(1);
        }
    }

    if (!summaryText) {
        console.error('[오류] 요약 텍스트가 비어 있습니다.');
        process.exit(1);
    }

    console.log(`[3/9] 요약 텍스트 읽기 완료 (${summaryText.length}자)`);

    // ── 3. 마크다운 콘텐츠 생성 ───────────────────────────────────────────
    console.log(`[4/9] 마크다운 콘텐츠 생성 중...`);

    const videoTitle = knowledgeData!.title || '(제목 없음)';
    const channelName = knowledgeData!.channelName || '(채널 없음)';
    const hasTranscript = knowledgeData!.hasTranscript ?? false;

    // publishedAt 처리
    let publishedAtStr = new Date().toISOString();
    if (knowledgeData!.publishedAt) {
        publishedAtStr = knowledgeData!.publishedAt.toDate().toISOString();
    }
    const videoDate = publishedAtStr.slice(0, 10);

    const markdownContent = buildMarkdownContent(
        summaryText,
        videoTitle,
        channelName,
        videoId,
        publishedAtStr,
        '(영상 길이 정보 없음)',
        hasTranscript
    );

    console.log(`[4/9] 마크다운 콘텐츠 생성 완료 (${markdownContent.length}자)`);

    // ── 4. Google Drive 업로드 ────────────────────────────────────────────
    console.log(`[5/9] Google Drive에 _요약.md 업로드 중...`);

    const driveClientId = process.env.DRIVE_CLIENT_ID;
    const driveClientSecret = process.env.DRIVE_CLIENT_SECRET;
    const driveRefreshToken = process.env.DRIVE_REFRESH_TOKEN;
    const rootFolderId = process.env.GOOGLE_DRIVE_ROOT_FOLDER_ID || process.env.GOOGLE_DRIVE_FOLDER_ID;

    if (!driveClientId || !driveClientSecret || !driveRefreshToken) {
        console.error('[오류] Drive OAuth 환경변수가 없습니다.');
        console.error('  DRIVE_CLIENT_ID, DRIVE_CLIENT_SECRET, DRIVE_REFRESH_TOKEN을 .env.local에 설정하세요.');
        process.exit(1);
    }

    if (!rootFolderId) {
        console.error('[오류] GOOGLE_DRIVE_ROOT_FOLDER_ID (또는 GOOGLE_DRIVE_FOLDER_ID) 환경변수가 없습니다.');
        process.exit(1);
    }

    let driveUrl = '';
    try {
        const driveOAuth2 = new google.auth.OAuth2(driveClientId, driveClientSecret);
        driveOAuth2.setCredentials({ refresh_token: driveRefreshToken });
        const drive = google.drive({ version: 'v3', auth: driveOAuth2 });

        driveUrl = await uploadMarkdownToDrive(
            drive,
            markdownContent,
            channelName,
            videoTitle,
            videoDate,
            rootFolderId,
            '_요약'
        );
        console.log(`[5/9] Drive 업로드 완료: ${driveUrl}`);
    } catch (err) {
        console.error('[오류] Google Drive 업로드 실패:', err);
        process.exit(1);
    }

    // ── 5. Gemini 임베딩 생성 ─────────────────────────────────────────────
    console.log(`[6/9] Gemini 임베딩 생성 중...`);

    const geminiApiKey = process.env.GEMINI_API_KEY;
    if (!geminiApiKey) {
        console.error('[오류] GEMINI_API_KEY 환경변수가 없습니다.');
        process.exit(1);
    }

    let embedding: number[] = [];
    try {
        const genAI = new GoogleGenerativeAI(geminiApiKey);
        const embedModel = genAI.getGenerativeModel({ model: 'gemini-embedding-001' });

        const embeddingResult = await embedModel.embedContent(
            summaryText.slice(0, 8000)  // 임베딩은 요약 텍스트 기반, 8000자 제한
        );
        embedding = embeddingResult.embedding.values;
        console.log(`[6/9] 임베딩 생성 완료 (차원: ${embedding.length})`);
    } catch (err) {
        console.error('[오류] Gemini 임베딩 생성 실패:', err);
        process.exit(1);
    }

    // ── 6. Firestore youtube_knowledge 업데이트 ───────────────────────────
    console.log(`[7/9] Firestore youtube_knowledge 업데이트 중...`);

    try {
        await knowledgeDocRef!.update({
            chunkText: summaryText.slice(0, 2000),
            embedding,
            driveUrl,
            summaryStatus: 'done',
            updatedAt: admin.firestore.FieldValue.serverTimestamp(),
        });
        console.log(`[7/9] youtube_knowledge 업데이트 완료`);
    } catch (err) {
        console.error('[오류] youtube_knowledge 업데이트 실패:', err);
        process.exit(1);
    }

    // ── 7. insurance_chunks에 요약 기반 청크 저장 ─────────────────────────
    console.log(`[8/9] insurance_chunks에 요약 청크 저장 중...`);

    try {
        // 기존 youtube sourceType 청크 중복 여부 확인 (동일 videoId)
        const existingChunk = await db
            .collection('insurance_chunks')
            .where('productId', '==', videoId)
            .where('sourceType', '==', 'youtube')
            .limit(1)
            .get();

        if (!existingChunk.empty) {
            // 기존 청크 업데이트
            const existingChunkRef = existingChunk.docs[0].ref;
            await existingChunkRef.update({
                chunkText: summaryText.slice(0, 2000),
                embedding,
                driveFileId: driveUrl,
                updatedAt: admin.firestore.FieldValue.serverTimestamp(),
            });
            console.log(`[8/9] 기존 insurance_chunks 문서 업데이트 완료 (id: ${existingChunk.docs[0].id})`);
        } else {
            // 신규 청크 저장
            const chunkRef = await db.collection('insurance_chunks').add({
                companyId: knowledgeData!.channelId || '',
                companyName: channelName,
                productId: videoId,
                productName: videoTitle,
                pageNumber: 0,
                chunkText: summaryText.slice(0, 2000),
                embedding,
                coverageNames: [],
                sourceType: 'youtube',
                effectiveDate: videoDate,
                driveFileId: driveUrl,
                conflictsWithPolicy: false,
                createdAt: admin.firestore.FieldValue.serverTimestamp(),
            });
            console.log(`[8/9] insurance_chunks 신규 저장 완료 (id: ${chunkRef.id})`);
        }
    } catch (err) {
        console.error('[오류] insurance_chunks 저장 실패:', err);
        process.exit(1);
    }

    // ── 8. youtube_summary_queue 처리 ────────────────────────────────────
    console.log(`[9/9] youtube_summary_queue 처리 중...`);

    try {
        const queueDocRef = db.collection('youtube_summary_queue').doc(videoId);
        const queueDoc = await queueDocRef.get();

        if (queueDoc.exists) {
            await queueDocRef.update({
                status: 'done',
                completedAt: admin.firestore.FieldValue.serverTimestamp(),
            });
            console.log(`[9/9] youtube_summary_queue 상태 → 'done' (videoId: ${videoId})`);
        } else {
            console.log(`[9/9] youtube_summary_queue에 해당 문서 없음 (스킵)`);
        }
    } catch (err) {
        // 큐 업데이트 실패는 치명적이지 않으므로 경고만 출력
        console.warn('[경고] youtube_summary_queue 업데이트 실패 (메인 작업은 완료됨):', err);
    }

    console.log(`\n=== 모든 작업 완료 ===`);
    console.log(`  videoId    : ${videoId}`);
    console.log(`  제목       : ${videoTitle}`);
    console.log(`  채널       : ${channelName}`);
    console.log(`  Drive URL  : ${driveUrl}`);
    console.log(`  임베딩 차원: ${embedding.length}`);
    console.log(`  요약 길이  : ${summaryText.length}자`);

    process.exit(0);
}

main().catch((err) => {
    console.error('[치명적 오류] 예기치 않은 오류:', err);
    process.exit(1);
});
