import { NextRequest, NextResponse } from 'next/server';
import { adminDb } from '@/lib/firebase-admin';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { GEMINI_SYSTEM_PROMPT, getDisclaimerText, validateAnswer, sanitizeAssertions, maskPII } from '@/lib/ai/answerValidator';
import { FieldValue } from 'firebase-admin/firestore';
import { verifyMember } from '@/lib/auth-middleware';
import { checkRateLimit, checkArchiveRateLimit } from '@/lib/security/rateLimiter';
import { checkInjection, sanitizeInput } from '@/lib/security/injectionFilter';
import { getFromCache, setToCache } from '@/lib/cache/queryCache';
import { getTodayUsage } from '@/lib/monitoring/costMonitor';
import { filterCurrentChunks, filterCurrentVersionMetadata, filterMetadataByDate } from '@/lib/ai/versionFilter';
import type { InsuranceMetadata, InsuranceChunk, InsuranceChunkArchive } from '@/types/firestore';

/**
 * Task E: B형 횡단 검색 — Gemini Embedding + Firestore Vector Search
 * Spec: docs/specs/260225-insuwiki-rag-spec-v2.md § 4-2, § 8, § 9
 *
 * 흐름:
 * 1. 질문 → Gemini text-embedding-004로 벡터화
 * 2. insurance_chunks에서 TOP 5 유사 청크 검색 (Firestore Vector Search)
 * 3. § 8-1 유사도 게이트 (동적 임계값: 상품 특정 시 0.92/0.80, 일반 쿼리 0.80/0.65)
 * 4. § 8-2 시스템 프롬프트로 Gemini 1.5 Flash 답변 생성
 * 5. § 8-3 validateAnswer() 통과 확인
 * 6. § 9 면책 문구 자동 삽입
 * 7. query_logs 감사 로그 저장
 *
 * CL-5: 검색 정밀도 개선
 * - 동적 임계값: productId/상품명 포함 시 TRUST=0.92, AMBIGUOUS=0.80
 * - 미등록 상품 사전 검증 (insurance_metadata 컬렉션)
 * - productId 있으면 검색 후 메타데이터 필터 우선 적용
 *
 * CL-7: 과거 약관 버전 조회
 * - targetDate(YYYY-MM) 파라미터로 과거 버전 검색 지원
 * - 과거 검색: insurance_chunks_archive 컬렉션, exact KNN(COSINE)
 * - 현행 검색: insurance_chunks 컬렉션, ANN(기존 로직)
 * - targetDate 서버사이드 검증 (형식, 미래 차단, 범위 2000-01 ~ 현재월)
 * - 과거 버전 전용 rate limit (checkArchiveRateLimit)
 * - 응답에 versionContext 워터마크 삽입
 */

// ── CL-7: targetDate 서버사이드 검증 ─────────────────────────────────
/**
 * targetDate(YYYY-MM) 유효성 검사
 * - YYYY-MM 형식만 허용
 * - 범위: 2000-01 ~ 현재월
 * - 미래 날짜 차단
 *
 * @returns null이면 유효, string이면 오류 메시지
 */
function validateTargetDate(targetDate: string): string | null {
    const FORMAT_RE = /^\d{4}-\d{2}$/;
    if (!FORMAT_RE.test(targetDate)) {
        return 'targetDate는 YYYY-MM 형식이어야 합니다.';
    }

    const [yearStr, monthStr] = targetDate.split('-');
    const year = parseInt(yearStr, 10);
    const month = parseInt(monthStr, 10);

    if (month < 1 || month > 12) {
        return 'targetDate의 월(MM)은 01~12 사이여야 합니다.';
    }

    if (year < 2000) {
        return 'targetDate는 2000-01 이후여야 합니다.';
    }

    // 현재월 기준 미래 차단 (YYYY-MM 사전식 비교 가능)
    const now = new Date();
    const currentYearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
    if (targetDate > currentYearMonth) {
        return `targetDate는 현재월(${currentYearMonth}) 이하여야 합니다.`;
    }

    return null;
}

// ── CL-5: 동적 임계값 타입 ────────────────────────────────────────────
interface DynamicThresholds {
    TRUST: number;
    AMBIGUOUS: number;
}

// 상품명/회사명 추출에 사용할 간단한 패턴 목록
// 보험사 이름, 상품명 식별 패턴 (예: "삼성생명 종신보험", "현대해상 하이카")
const PRODUCT_SIGNAL_PATTERNS = [
    /([가-힣a-zA-Z0-9]+생명|[가-힣a-zA-Z0-9]+화재|[가-힣a-zA-Z0-9]+해상|[가-힣a-zA-Z0-9]+손해보험)/,
    /(종신보험|연금보험|암보험|건강보험|실손보험|CI보험|변액보험|저축보험)/,
    /(플랜|플러스|케어|라이프|프리미엄|에이스|스마트|골드|다이렉트)/i,
];

/**
 * CL-5: 동적 임계값 결정 함수
 * - productId 또는 쿼리에 상품명 시그널이 있으면 더 엄격한 임계값 사용
 *   (상품을 특정했으므로 정확도 기대치가 높음)
 * - 일반 쿼리는 더 관대한 임계값 사용
 *
 * 상품 특정 시: TRUST=0.92, AMBIGUOUS=0.80
 * 일반 쿼리 시: TRUST=0.80, AMBIGUOUS=0.65
 */
function getDynamicThresholds(question: string, productId?: string): DynamicThresholds {
    // productId가 명시적으로 전달된 경우
    if (productId && productId.trim().length > 0) {
        return { TRUST: 0.92, AMBIGUOUS: 0.80 };
    }

    // 쿼리 내 상품명/회사명 패턴 감지
    const hasProductSignal = PRODUCT_SIGNAL_PATTERNS.some(pattern => pattern.test(question));
    if (hasProductSignal) {
        return { TRUST: 0.92, AMBIGUOUS: 0.80 };
    }

    // 일반 쿼리
    return { TRUST: 0.80, AMBIGUOUS: 0.65 };
}

/**
 * CL-5: 쿼리에서 상품명/회사명 후보 추출
 * insurance_metadata 컬렉션 조회를 위한 전처리
 */
function extractProductNameCandidates(question: string): string[] {
    const candidates: string[] = [];

    for (const pattern of PRODUCT_SIGNAL_PATTERNS) {
        const match = question.match(pattern);
        if (match && match[0]) {
            candidates.push(match[0]);
        }
    }

    return [...new Set(candidates)];
}

/**
 * CL-5: 미등록 상품 사전 검증
 * - productId가 주어진 경우: insurance_metadata에서 직접 조회
 * - 쿼리에서 상품명/회사명 추출된 경우: productName/companyName으로 조회
 * - 미등록이면 false 반환
 */
async function checkProductRegistered(question: string, productId?: string): Promise<boolean> {
    const db = adminDb;

    try {
        // productId로 직접 조회
        if (productId && productId.trim().length > 0) {
            const metadataSnap = await db.collection('insurance_metadata')
                .where('productId', '==', productId.trim())
                .where('isActive', '==', true)
                .limit(1)
                .get();

            return !metadataSnap.empty;
        }

        // 쿼리에서 상품명/회사명 후보 추출 후 조회
        const candidates = extractProductNameCandidates(question);
        if (candidates.length === 0) {
            // 특정 상품을 지칭하는 패턴이 없으면 검증 통과 (일반 질문)
            return true;
        }

        for (const candidate of candidates) {
            // productName 또는 companyName에 후보가 포함된 문서 탐색
            const byProductName = await db.collection('insurance_metadata')
                .where('productName', '>=', candidate)
                .where('productName', '<=', candidate + '\uf8ff')
                .where('isActive', '==', true)
                .limit(1)
                .get();

            if (!byProductName.empty) return true;

            const byCompanyName = await db.collection('insurance_metadata')
                .where('companyName', '>=', candidate)
                .where('companyName', '<=', candidate + '\uf8ff')
                .where('isActive', '==', true)
                .limit(1)
                .get();

            if (!byCompanyName.empty) return true;
        }

        // 패턴은 감지됐지만 메타데이터에서 찾지 못함 → 미등록
        return false;
    } catch (e) {
        // 검증 실패 시 통과 처리 (서비스 가용성 우선)
        console.warn('미등록 상품 검증 중 오류 (통과 처리):', e);
        return true;
    }
}

export async function POST(req: NextRequest) {
    try {
        // Firebase ID Token 검증
        const authResult = await verifyMember(req);
        if (authResult instanceof NextResponse) return authResult;
        const userId = authResult.uid;

        // ── Rate Limit 체크 ────────────────────────────────────────────────
        const clientIp =
            req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
            req.headers.get('x-real-ip') ||
            'unknown';
        const rateLimitResult = await checkRateLimit(userId, clientIp);
        if (!rateLimitResult.allowed) {
            return NextResponse.json(
                { error: '요청 한도를 초과했습니다. 잠시 후 다시 시도해 주세요.' },
                {
                    status: 429,
                    headers: {
                        'Retry-After': String(rateLimitResult.retryAfter ?? 60),
                    },
                }
            );
        }

        // ── 일일 하드 리밋 체크 ──────────────────────────────────────────────
        const MAX_DAILY_QUERIES = parseInt(process.env.MAX_DAILY_QUERIES || '5000');
        const dailyUsage = await getTodayUsage();
        if (dailyUsage.totalQueries >= MAX_DAILY_QUERIES || dailyUsage.isOverBudget) {
            return NextResponse.json(
                { error: '일일 API 사용량 한도를 초과했습니다. 내일 다시 시도해주세요.' },
                { status: 429 }
            );
        }

        // ── Body 파싱 ──────────────────────────────────────────────────────
        const body = await req.json();
        // CL-5: productId를 body에서 추가로 받음
        // CL-7: targetDate(YYYY-MM) — null이면 현행 검색, 값이 있으면 과거 버전 검색
        const { question: rawQuestion, sessionId, productId, targetDate = null } = body as {
            question: string;
            sessionId?: string;
            productId?: string;
            targetDate: string | null;
        };

        if (!rawQuestion) {
            return NextResponse.json({ error: 'question은 필수입니다.' }, { status: 400 });
        }

        // ── CL-7: targetDate 서버사이드 검증 ──────────────────────────────
        if (targetDate !== null) {
            const dateError = validateTargetDate(targetDate);
            if (dateError) {
                return NextResponse.json({ error: dateError }, { status: 400 });
            }

            // 과거 버전 전용 rate limit 체크 (일반 rate limit과 별도 적용)
            const archiveRateLimitResult = await checkArchiveRateLimit(userId, clientIp);
            if (!archiveRateLimitResult.allowed) {
                return NextResponse.json(
                    { error: '과거 버전 조회 한도를 초과했습니다. 잠시 후 다시 시도해 주세요.' },
                    {
                        status: 429,
                        headers: {
                            'Retry-After': String(archiveRateLimitResult.retryAfter ?? 60),
                        },
                    }
                );
            }
        }

        // ── 입력 정제 (sanitizeInput) ──────────────────────────────────────
        const sanitizedQuestion = sanitizeInput(rawQuestion as string);

        // ── 인젝션 탐지 (checkInjection) ──────────────────────────────────
        const injectionResult = checkInjection(sanitizedQuestion);
        if (!injectionResult.safe) {
            return NextResponse.json(
                {
                    error: '허용되지 않는 입력이 감지되었습니다.',
                    severity: injectionResult.severity,
                    reason: injectionResult.reason,
                },
                { status: 400 }
            );
        }

        // 정제된 쿼리를 이후 로직에서 사용
        const question = sanitizedQuestion;

        const apiKey = process.env.GEMINI_API_KEY;
        if (!apiKey) return NextResponse.json({ error: 'GEMINI_API_KEY 없음' }, { status: 500 });

        // ── CL-5: 미등록 상품 사전 검증 ─────────────────────────────────
        const isRegistered = await checkProductRegistered(question, productId);
        if (!isRegistered) {
            return NextResponse.json({
                answer: '해당 상품은 등록되지 않았습니다. 관리자에게 약관 등록을 요청해 주세요.',
                sources: [],
                confidenceScore: 0,
                gate: 'UNREGISTERED',
            });
        }

        // ── CL-5: 동적 임계값 결정 ───────────────────────────────────────
        const thresholds = getDynamicThresholds(question, productId);

        const db = adminDb;
        const genAI = new GoogleGenerativeAI(apiKey);

        // ── CL-7: 현행/과거 메타데이터 조회 (버전 필터용) ──────────────
        // - targetDate가 없으면 현행(end 없는 버전) 필터 적용
        // - targetDate가 있으면 해당 시점에 유효한 메타데이터 필터 적용
        let currentMetadata: InsuranceMetadata[] = [];
        // 캐시 버전: targetDate가 있으면 targetDate 자체를 버전 키로 사용
        let cacheVersion = targetDate ? `archive_${targetDate}` : 'all';
        try {
            let metaQuery = db.collection('insurance_metadata')
                .where('isActive', '==', true) as FirebaseFirestore.Query;
            if (productId && productId.trim().length > 0) {
                metaQuery = metaQuery.where('productId', '==', productId.trim());
            }
            const metaSnap = await metaQuery.get();
            const allMetadata = metaSnap.docs.map(
                (doc) => ({ id: doc.id, ...doc.data() } as InsuranceMetadata)
            );
            if (targetDate) {
                // CL-7: 과거 검색 — filterMetadataByDate로 해당 시점에 유효한 메타데이터 추출
                currentMetadata = filterMetadataByDate(allMetadata, targetDate);
            } else {
                // 현행 검색 — end가 없는 현행 버전만 추출
                currentMetadata = filterCurrentVersionMetadata(allMetadata);
                // 캐시 버전 키: 현행 메타데이터의 effectiveDateRange.start 중 첫 번째
                const firstDate = currentMetadata[0]?.effectiveDateRange?.start;
                if (firstDate) cacheVersion = firstDate;
            }
        } catch (e) {
            // 버전 메타데이터 조회 실패 시 버전 필터 없이 진행 (서비스 가용성 우선)
            console.warn('메타데이터 조회 실패 (버전 필터 스킵):', e);
        }

        // ── 캐시 조회 (productId가 있을 때만 캐시 사용) ───────────────────
        if (productId && productId.trim().length > 0) {
            try {
                const cached = await getFromCache(question, productId.trim(), cacheVersion);
                if (cached) {
                    return NextResponse.json({
                        answer: cached.result,
                        sources: cached.citations ?? [],
                        confidenceScore: cached.confidenceScore ?? 0,
                        gate: 'TRUST',
                        cached: true,
                    });
                }
            } catch (e) {
                // 캐시 조회 실패 시 통과 처리 (서비스 가용성 우선)
                console.warn('캐시 조회 실패 (통과 처리):', e);
            }
        }

        // ── 1. 용어 정규화 (insurance_terms 동의어 확장) §11-2 ─────────────
        const expandedQuestion = await expandQueryWithTerms(question);

        // ── 2. Gemini Embedding으로 질문 벡터화 ───────────────────────────
        const embedModel = genAI.getGenerativeModel({ model: 'text-embedding-004' });
        const embeddingResult = await embedModel.embedContent(expandedQuestion);
        const queryEmbedding = embeddingResult.embedding.values;

        // ── 3. Firestore Vector Search ─────────────────────────────────────
        // CL-7: targetDate가 있으면 insurance_chunks_archive(exact KNN),
        //       없으면 insurance_chunks(ANN, 기존 로직)
        // NOTE: Firestore Vector Search는 Firebase Console에서
        //       각 컬렉션의 embedding 필드에 벡터 인덱스 생성 필요
        const isHistoricalSearch = targetDate !== null;
        const chunksRef = isHistoricalSearch
            ? db.collection('insurance_chunks_archive')
            : db.collection('insurance_chunks');

        const vectorQuery = chunksRef.findNearest({
            vectorField: 'embedding',
            queryVector: queryEmbedding,
            limit: 10,
            distanceMeasure: 'COSINE',
            distanceResultField: '_distance',
        });

        const snapshot = await vectorQuery.get();

        if (snapshot.empty) {
            return NextResponse.json({
                answer: '해당 내용을 약관에서 찾을 수 없습니다. 더 구체적인 질문이나 회사/상품을 선택해 주세요.',
                sources: [],
                confidenceScore: 0,
            });
        }

        // ── 4. 유사도 게이트 §8-1 ────────────────────────────────────────
        // COSINE distance: 0=완전 동일, 2=완전 반대 → 유사도 = 1 - distance/2
        let chunks = snapshot.docs.map((doc: any) => ({
            ...doc.data(),
            id: doc.id,
            similarity: 1 - (doc.data()._distance ?? 1),
        }));

        // ── CL-5: productId 메타데이터 필터 우선 적용 ────────────────────
        // Firestore Vector Search의 findNearest에서 직접 필터링이 지원되지 않으므로
        // 검색 결과에서 후처리 필터링
        if (productId && productId.trim().length > 0) {
            const filteredByProduct = chunks.filter((c: any) => c.productId === productId.trim());
            // 필터링 결과가 있을 때만 적용 (결과가 없으면 원본 유지하여 서비스 가용성 보장)
            if (filteredByProduct.length > 0) {
                chunks = filteredByProduct;
            }
        }

        // ── 버전 필터 적용 ────────────────────────────────────────────────
        // - 현행 검색(!targetDate): filterCurrentChunks로 현행 버전 청크만 유지
        // - 과거 검색(targetDate): filterMetadataByDate 결과를 기반으로 해당 시점 청크 필터
        //   (archive 컬렉션에서 검색했으므로 effectiveDate 기준으로 추가 필터링)
        if (currentMetadata.length > 0) {
            try {
                const filtered = filterCurrentChunks(
                    chunks as (InsuranceChunk | InsuranceChunkArchive)[],
                    currentMetadata
                );
                // 필터링 결과가 있을 때만 적용 (결과가 없으면 원본 유지하여 서비스 가용성 보장)
                if (filtered.length > 0) {
                    chunks = filtered;
                }
            } catch (e) {
                // 버전 필터 실패 시 통과 처리 (서비스 가용성 우선)
                console.warn('버전 필터 적용 실패 (통과 처리):', e);
            }
        }

        // ── CL-5: 동적 임계값으로 유사도 게이트 적용 ────────────────────
        // (기존 고정값 SIMILARITY_THRESHOLDS 대신 thresholds 사용)
        const trustedChunks = chunks.filter((c: any) => c.similarity >= thresholds.TRUST);
        const ambiguousChunks = chunks.filter((c: any) =>
            c.similarity >= thresholds.AMBIGUOUS && c.similarity < thresholds.TRUST
        );

        // 신뢰할 수 있는 청크가 없으면 답변 거부
        if (trustedChunks.length === 0 && ambiguousChunks.length === 0) {
            return NextResponse.json({
                answer: '해당 내용을 약관에서 찾을 수 없습니다. 더 구체적인 질문을 입력해 주세요.',
                sources: [],
                confidenceScore: 0,
                gate: 'REJECT',
            });
        }

        // Disambiguation 유도 (신뢰 청크 없고 모호한 청크만 있을 때)
        if (trustedChunks.length === 0 && ambiguousChunks.length > 0) {
            const candidateProducts = [...new Set(ambiguousChunks.map((c: any) =>
                `${c.companyName} · ${c.productName}`
            ))].slice(0, 3);
            return NextResponse.json({
                answer: null,
                gate: 'DISAMBIGUATION',
                disambiguationCase: 'PRODUCT_AMBIGUOUS',
                candidates: candidateProducts,
                message: '여러 상품에서 관련 내용이 발견됐습니다. 어떤 상품 기준으로 확인할까요?',
            });
        }

        // TOP 5 신뢰 청크 컨텍스트 구성
        const contextChunks = trustedChunks.slice(0, 5);
        const contextText = contextChunks.map((c: any, i: number) =>
            `[출처 ${i + 1}] ${c.companyName} · ${c.productName} (${c.pageNumber}p)\n${c.chunkText}`
        ).join('\n\n---\n\n');

        // ── CL-9: 별표 키워드 트리거 → 별표 데이터 합산 ──────────────────
        let appendixData: any[] = [];
        if (hasAppendixTrigger(question)) {
            appendixData = await fetchAppendixData(question, productId);
        }

        // 별표 데이터가 있으면 컨텍스트에 추가
        let appendixContext = '';
        if (appendixData.length > 0) {
            appendixContext = '\n\n---\n\n[별표/부속서류 참고자료]\n' +
                appendixData.map((a: any, i: number) => {
                    const content = a.markdownTable || a.content || '';
                    return `[별표 ${i + 1}] ${a.appendixType || '기타'}\n${content.slice(0, 2000)}`;
                }).join('\n\n');
        }

        // ── 5. Gemini Flash 답변 생성 §8-2 ───────────────────────────────
        const chatModel = genAI.getGenerativeModel({
            model: 'gemini-2.0-flash', // B형 횡단 검색 답변 — 빠르고 안정적
            systemInstruction: GEMINI_SYSTEM_PROMPT,
        });

        const prompt = `다음 보험 약관 내용을 참고하여 질문에 답변해 주세요.

[참고 약관 내용]
${contextText}${appendixContext}

[질문]
${question}`;

        const result = await chatModel.generateContent(prompt);
        let answerText = result.response.text();

        // ── 6. 답변 검증 §8-3 ─────────────────────────────────────────────
        const validation = validateAnswer(answerText);
        if (!validation.valid) {
            console.warn('⚠️ 답변 검증 실패:', validation.reason);
            answerText = '제공된 약관에서 해당 내용을 확인할 수 없습니다. 약관 원문을 직접 확인해 주세요.';
        }

        // ── 6-b. 출력 필터: 단정 표현 치환 + PII 마스킹 ─────────────────
        answerText = sanitizeAssertions(answerText);
        answerText = maskPII(answerText);

        // ── 7. 면책 문구 자동 삽입 §9 ────────────────────────────────────
        const disclaimer = getDisclaimerText('policy', {});
        const finalAnswer = answerText.includes('※') ? answerText : answerText + '\n\n' + disclaimer;

        // 출처 배지 목록 구성
        const sources = contextChunks.map((c: any) => ({
            companyName: c.companyName,
            productName: c.productName,
            pageNumber: c.pageNumber,
            sourceType: c.sourceType as 'policy' | 'newsletter' | 'youtube' | 'appendix',
            similarity: Math.round(c.similarity * 100) / 100,
        }));

        const avgConfidence = contextChunks.reduce((sum: number, c: any) => sum + c.similarity, 0) / contextChunks.length;

        // ── 8. query_logs 감사 로그 §13 ──────────────────────────────────
        await db.collection('query_logs').add({
            agentId: userId,
            sessionId: sessionId || '',
            question,
            queryType: 'VECTOR_SEARCH',
            sourceDocs: contextChunks.map((c: any) => c.driveFileId),
            answer: finalAnswer,
            confidenceScore: avgConfidence,
            hasDisclaimerAttached: true,
            timestamp: FieldValue.serverTimestamp(),
        });

        // ── 캐시 저장 (productId가 있을 때만) ────────────────────────────
        if (productId && productId.trim().length > 0) {
            try {
                await setToCache(
                    question,
                    productId.trim(),
                    cacheVersion,
                    finalAnswer,
                    {
                        citations: sources.map(
                            (s: { companyName: string; productName: string; pageNumber: number }) => `${s.companyName} · ${s.productName} ${s.pageNumber}p`
                        ),
                        confidenceScore: Math.round(avgConfidence * 100) / 100,
                    }
                );
            } catch (e) {
                // 캐시 저장 실패 시 통과 처리 (서비스 가용성 우선)
                console.warn('캐시 저장 실패 (통과 처리):', e);
            }
        }

        return NextResponse.json({
            answer: finalAnswer,
            sources,
            confidenceScore: Math.round(avgConfidence * 100) / 100,
            gate: 'TRUST',
            ...(appendixData.length > 0 && {
                appendix: appendixData.map((a: any) => ({
                    appendixType: a.appendixType,
                    content: a.markdownTable || a.content?.slice(0, 500) || '',
                })),
            }),
            // CL-7: 과거 버전 조회 시 버전 컨텍스트 워터마크 삽입
            ...(targetDate && {
                versionContext: {
                    targetDate,
                    isHistorical: true,
                    disclaimer: `이 정보는 ${targetDate} 기준 약관입니다. 현행 약관과 다를 수 있습니다.`,
                },
            }),
        });

    } catch (error: any) {
        console.error('Vector Search Error:', error);
        return NextResponse.json({ error: error.message }, { status: 500 });
    }
}

// ── CL-9: 별표 키워드 트리거 ────────────────────────────────────────────
const APPENDIX_TRIGGER_KEYWORDS = [
    '수술분류', '장해등급', '장해분류', '질병코드', '질병분류',
    '몇 종', '수술 종류', '수술종류', '별표', '부속서류',
];

function hasAppendixTrigger(question: string): boolean {
    return APPENDIX_TRIGGER_KEYWORDS.some(kw => question.includes(kw));
}

async function fetchAppendixData(question: string, productId?: string): Promise<any[]> {
    try {
        const db = adminDb;
        let appendixQuery: FirebaseFirestore.Query = db.collection('insurance_appendices');

        if (productId && productId.trim().length > 0) {
            appendixQuery = appendixQuery.where('productId', '==', productId.trim());
        }

        const snapshot = await appendixQuery.limit(5).get();
        if (snapshot.empty) return [];

        return snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data(),
        }));
    } catch (e) {
        console.warn('별표 데이터 조회 실패 (통과 처리):', e);
        return [];
    }
}

// ── 용어 정규화: insurance_terms 동의어 확장 §11-2 ─────────────────────
async function expandQueryWithTerms(question: string): Promise<string> {
    try {
        const db = adminDb;
        // 모든 용어를 메모리 캐시로 불러와서 동의어 확장
        // 실제 운영 시 인메모리 캐싱 권장 (1시간 TTL)
        const termsSnap = await db.collection('insurance_terms')
            .where('verified', '==', true)
            .limit(200)
            .get();

        let expanded = question;
        termsSnap.docs.forEach((doc: FirebaseFirestore.QueryDocumentSnapshot) => {
            const term = doc.data();
            if (term.commonAliases) {
                term.commonAliases.forEach((alias: string) => {
                    if (question.includes(alias) && !question.includes(term.term)) {
                        expanded += ` ${term.term}`;
                    }
                });
            }
        });

        return expanded;
    } catch (e) {
        console.warn('용어 정규화 실패 (원본 사용):', e);
        return question;
    }
}
