/**
 * evaluate-static-matching.ts 핵심 로직 단위 테스트
 *
 * 테스트 대상:
 *  - verifyMatchResult: confidence 100/90/85/70 케이스
 *  - findMatchingTerms: 대표 케이스 (exact match, 빈 terms)
 *
 * Firebase 의존성 없이 순수 함수만 복사하여 테스트합니다.
 */

import { describe, it, expect } from 'vitest';

// ============================================================
// 타입 정의 (evaluate-static-matching.ts에서 복사)
// ============================================================

type LinkMethod = 'manual' | 'static' | 'embedding' | 'semantic';

interface InsuranceTerm {
    id: string;
    term: string;
    definition: string;
    commonAliases: string[];
    icdCodes?: string[];
    companyId: string;
    productId: string;
    pageNumber: number;
    verified: boolean;
    createdAt: any;
}

interface MatchResult {
    termId: string;
    term: string;
    method: LinkMethod;
    confidence: number;
    explanation: string;
}

interface VerifiedMatchResult extends MatchResult {
    verified: boolean;
    verifyReason: string;
}

// ============================================================
// findMatchingTerms 순수 함수 복사 (evaluate-static-matching.ts에서)
// ============================================================

function findMatchingTerms(
    content: string,
    terms: InsuranceTerm[],
    existingLinkIds: string[],
    normalizeMap: Record<string, string> = {}
): MatchResult[] {
    const results: MatchResult[] = [];
    const contentLower = content.toLowerCase();
    const contentNoSpace = contentLower.replace(/\s/g, '');
    const seenTermIds = new Set<string>();
    const excludeIds = new Set(existingLinkIds);

    for (const term of terms) {
        if (excludeIds.has(term.id)) continue;
        if (seenTermIds.has(term.id)) continue;

        const termLower = term.term.toLowerCase();

        // 1. exact match → confidence: 100
        if (contentLower.includes(termLower)) {
            results.push({
                termId: term.id,
                term: term.term,
                method: 'static',
                confidence: 100,
                explanation: `exact match: "${term.term}"`,
            });
            seenTermIds.add(term.id);
            continue;
        }

        // 2. alias match (commonAliases) → confidence: 90
        let aliasMatched = false;
        for (const alias of term.commonAliases) {
            const aliasLower = alias.toLowerCase();
            if (contentLower.includes(aliasLower)) {
                results.push({
                    termId: term.id,
                    term: term.term,
                    method: 'static',
                    confidence: 90,
                    explanation: `alias match: "${alias}" → "${term.term}"`,
                });
                seenTermIds.add(term.id);
                aliasMatched = true;
                break;
            }
        }
        if (aliasMatched) continue;

        // normalizeMap 적용 후 정규화된 용어 계산
        let normalizedTermLower = termLower;
        if (normalizeMap[termLower]) {
            normalizedTermLower = normalizeMap[termLower].toLowerCase();
        }
        const normalizedNoSpace = normalizedTermLower.replace(/\s/g, '');

        // 3. 공백 제거 후 match → confidence: 85
        if (normalizedNoSpace.length > 0 && contentNoSpace.includes(normalizedNoSpace)) {
            results.push({
                termId: term.id,
                term: term.term,
                method: 'static',
                confidence: 85,
                explanation: `공백 제거 match: "${term.term}"`,
            });
            seenTermIds.add(term.id);
            continue;
        }

        // 4. substring 포함 체크 → confidence: 70 (3자 이상 용어만)
        if (termLower.length >= 3) {
            const contentWords = contentLower.split(/[\s.,!?;:()[\]{}'"]/);
            const substringMatched = contentWords.some(word => {
                if (word.length < 2) return false;
                return termLower.startsWith(word) && word.length >= 2;
            });
            if (substringMatched) {
                results.push({
                    termId: term.id,
                    term: term.term,
                    method: 'static',
                    confidence: 70,
                    explanation: `substring match: "${term.term}"`,
                });
                seenTermIds.add(term.id);
            }
        }
    }

    return results;
}

// ============================================================
// verifyMatchResult 순수 함수 복사 (evaluate-static-matching.ts에서)
// ============================================================

function verifyMatchResult(
    result: MatchResult,
    content: string,
    terms: InsuranceTerm[]
): VerifiedMatchResult {
    const contentLower = content.toLowerCase();
    const contentNoSpace = contentLower.replace(/\s/g, '');
    const term = terms.find(t => t.id === result.termId);

    if (!term) {
        return {
            ...result,
            verified: false,
            verifyReason: '용어 정보 없음',
        };
    }

    const termLower = term.term.toLowerCase();

    switch (result.confidence) {
        case 100: {
            // exact: content에 term이 포함되어야 함
            const ok = contentLower.includes(termLower);
            return {
                ...result,
                verified: ok,
                verifyReason: ok
                    ? `content에 "${term.term}" 포함 확인`
                    : `content에 "${term.term}" 미포함 (검증 실패)`,
            };
        }
        case 90: {
            // alias: content에 alias가 포함되어야 함
            // explanation에서 alias 추출: `alias match: "ALIAS" → "TERM"`
            const aliasMatch = result.explanation.match(/alias match: "([^"]+)"/);
            const matchedAlias = aliasMatch ? aliasMatch[1] : '';
            if (matchedAlias) {
                const ok = contentLower.includes(matchedAlias.toLowerCase());
                return {
                    ...result,
                    verified: ok,
                    verifyReason: ok
                        ? `content에 alias "${matchedAlias}" 포함 확인`
                        : `content에 alias "${matchedAlias}" 미포함 (검증 실패)`,
                };
            }
            // alias 추출 실패 시 commonAliases 전체 검사
            const anyAliasOk = term.commonAliases.some(a =>
                contentLower.includes(a.toLowerCase())
            );
            return {
                ...result,
                verified: anyAliasOk,
                verifyReason: anyAliasOk
                    ? 'commonAliases 중 하나 포함 확인'
                    : 'commonAliases 모두 미포함 (검증 실패)',
            };
        }
        case 85: {
            // space-removed: content 공백 제거 후 term이 포함
            const termNoSpace = termLower.replace(/\s/g, '');
            const ok = termNoSpace.length > 0 && contentNoSpace.includes(termNoSpace);
            return {
                ...result,
                verified: ok,
                verifyReason: ok
                    ? `공백 제거 후 content에 "${term.term}" 포함 확인`
                    : `공백 제거 후에도 "${term.term}" 미포함 (검증 실패)`,
            };
        }
        case 70: {
            // substring: content의 단어 중 term의 앞부분과 일치하는지 재확인
            const contentWords = contentLower.split(/[\s.,!?;:()[\]{}'"]/);
            const substringOk = contentWords.some(word => {
                if (word.length < 2) return false;
                return termLower.startsWith(word) && word.length >= 2;
            });
            return {
                ...result,
                verified: substringOk,
                verifyReason: substringOk
                    ? `substring 매칭 확인 (검증 보류 - false positive 가능)`
                    : `substring 매칭 재검증 실패`,
            };
        }
        default:
            return {
                ...result,
                verified: false,
                verifyReason: `알 수 없는 confidence 값: ${result.confidence}`,
            };
    }
}

// ============================================================
// 테스트 헬퍼
// ============================================================

const makeTerm = (overrides: Partial<InsuranceTerm> & { id: string; term: string }): InsuranceTerm => ({
    id: overrides.id,
    term: overrides.term,
    definition: overrides.definition ?? '테스트 용어 정의',
    commonAliases: overrides.commonAliases ?? [],
    icdCodes: overrides.icdCodes,
    companyId: overrides.companyId ?? 'company1',
    productId: overrides.productId ?? 'product1',
    pageNumber: overrides.pageNumber ?? 1,
    verified: overrides.verified ?? true,
    createdAt: overrides.createdAt ?? null,
});

// ============================================================
// 테스트
// ============================================================

describe('evaluate-static-matching: verifyMatchResult', () => {

    // ----------------------------------------------------------
    // confidence 100 (exact) 케이스
    // ----------------------------------------------------------
    describe('confidence 100 (exact match)', () => {

        it('[TP] content에 term이 포함되면 verified=true', () => {
            const term = makeTerm({ id: 'term1', term: '뇌졸중' });
            const content = '이 약관은 뇌졸중 진단 시 보험금을 지급합니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '뇌졸중',
                method: 'static',
                confidence: 100,
                explanation: 'exact match: "뇌졸중"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(true);
            expect(verified.verifyReason).toContain('포함 확인');
        });

        it('[FP] content에 term이 없으면 verified=false', () => {
            const term = makeTerm({ id: 'term1', term: '뇌졸중' });
            // content에 '뇌졸중' 없음 — 매칭 결과 자체가 잘못 생성된 상황
            const content = '이 약관은 암 진단 시 보험금을 지급합니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '뇌졸중',
                method: 'static',
                confidence: 100,
                explanation: 'exact match: "뇌졸중"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(false);
            expect(verified.verifyReason).toContain('미포함');
        });
    });

    // ----------------------------------------------------------
    // confidence 90 (alias) 케이스
    // ----------------------------------------------------------
    describe('confidence 90 (alias match)', () => {

        it('[TP] explanation에서 추출한 alias가 content에 있으면 verified=true', () => {
            const term = makeTerm({ id: 'term1', term: '심근경색', commonAliases: ['심장마비'] });
            const content = '갑작스러운 심장마비로 응급 입원했습니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '심근경색',
                method: 'static',
                confidence: 90,
                explanation: 'alias match: "심장마비" → "심근경색"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(true);
            expect(verified.verifyReason).toContain('심장마비');
            expect(verified.verifyReason).toContain('포함 확인');
        });

        it('[FP] explanation에서 추출한 alias가 content에 없으면 verified=false', () => {
            const term = makeTerm({ id: 'term1', term: '심근경색', commonAliases: ['심장마비'] });
            // content에 '심장마비' 없음
            const content = '고혈압 치료를 위해 내원했습니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '심근경색',
                method: 'static',
                confidence: 90,
                explanation: 'alias match: "심장마비" → "심근경색"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(false);
            expect(verified.verifyReason).toContain('미포함');
        });
    });

    // ----------------------------------------------------------
    // confidence 85 (space-removed) 케이스
    // ----------------------------------------------------------
    describe('confidence 85 (space-removed match)', () => {

        it('[TP] 공백 제거 후 content에 term이 포함되면 verified=true', () => {
            const term = makeTerm({ id: 'term1', term: '암 보험금', commonAliases: [] });
            // content에는 공백 없이 "암보험금"으로 붙어 있음
            const content = '이 약관은 암보험금 지급 기준을 명시합니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '암 보험금',
                method: 'static',
                confidence: 85,
                explanation: '공백 제거 match: "암 보험금"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(true);
            expect(verified.verifyReason).toContain('공백 제거 후');
        });
    });

    // ----------------------------------------------------------
    // confidence 70 (substring) 케이스
    // ----------------------------------------------------------
    describe('confidence 70 (substring match)', () => {

        it('[TP] content의 단어가 term의 앞부분과 일치하면 verified=true', () => {
            const term = makeTerm({ id: 'term1', term: '뇌졸중진단', commonAliases: [] });
            // "뇌졸중"이 "뇌졸중진단"의 앞부분과 일치
            const content = '환자는 뇌졸중 의심 증상으로 입원했습니다.';
            const result: MatchResult = {
                termId: 'term1',
                term: '뇌졸중진단',
                method: 'static',
                confidence: 70,
                explanation: 'substring match: "뇌졸중진단"',
            };

            const verified = verifyMatchResult(result, content, [term]);

            expect(verified.verified).toBe(true);
            expect(verified.verifyReason).toContain('substring 매칭 확인');
        });
    });

    // ----------------------------------------------------------
    // termId가 terms 배열에 없는 경우
    // ----------------------------------------------------------
    describe('term 정보 없음', () => {

        it('termId에 해당하는 term이 없으면 verified=false, verifyReason에 안내 포함', () => {
            const result: MatchResult = {
                termId: 'nonexistent',
                term: '존재하지않는용어',
                method: 'static',
                confidence: 100,
                explanation: 'exact match: "존재하지않는용어"',
            };

            const verified = verifyMatchResult(result, '테스트 content', []);

            expect(verified.verified).toBe(false);
            expect(verified.verifyReason).toBe('용어 정보 없음');
        });
    });
});

describe('evaluate-static-matching: findMatchingTerms', () => {

    // ----------------------------------------------------------
    // exact match 기본 동작
    // ----------------------------------------------------------
    it('용어가 content에 그대로 있으면 confidence 100 반환', () => {
        const terms = [
            makeTerm({ id: 'term1', term: '뇌졸중', commonAliases: [] }),
        ];
        const content = '이 문서는 뇌졸중에 관한 내용을 다룹니다.';

        const results = findMatchingTerms(content, terms, []);

        expect(results).toHaveLength(1);
        expect(results[0].termId).toBe('term1');
        expect(results[0].confidence).toBe(100);
        expect(results[0].method).toBe('static');
        expect(results[0].explanation).toContain('exact match');
    });

    // ----------------------------------------------------------
    // 빈 terms → 빈 결과
    // ----------------------------------------------------------
    it('terms가 빈 배열이면 결과도 빈 배열', () => {
        const content = '뇌졸중과 고혈압에 대한 내용입니다.';

        const results = findMatchingTerms(content, [], []);

        expect(results).toHaveLength(0);
    });
});
