/**
 * pdfIndexing.ts 별표/부속서류 섹션 감지 로직 테스트
 *
 * 테스트 대상 (pdfIndexing.ts에서 export):
 *  - classifyAppendixType : 수술분류표/장해분류표/질병분류/기타 분류
 *  - isAppendixSection    : 별표/부표/부속서류 패턴 감지
 *  - separateAppendixSections : 본문과 별표 섹션 분리
 *
 * vitest로 실행 (nextapp/vitest.config.ts가 ../functions/src/**를 포함)
 *
 * 모킹: pdfIndexing.ts가 firebase-admin, firebase-functions 등 외부 의존성을
 * import하므로 nextapp vitest 환경에서 실행 시 해당 모듈들을 모킹해야 함.
 */

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

// ── 외부 의존성 모킹 (pdfIndexing.ts의 최상위 import에 대응) ──────────────
vi.mock('firebase-admin', () => ({
    default: { apps: [], initializeApp: vi.fn(), credential: { cert: vi.fn() } },
    apps: [],
    initializeApp: vi.fn(),
    credential: { cert: vi.fn() },
}));

vi.mock('firebase-functions/v2/firestore', () => ({
    onDocumentCreated: vi.fn(() => vi.fn()),
}));

vi.mock('@google/generative-ai', () => ({
    GoogleGenerativeAI: vi.fn(() => ({
        getGenerativeModel: vi.fn(),
    })),
}));

vi.mock('@google/generative-ai/server', () => ({
    GoogleAIFileManager: vi.fn(() => ({})),
}));

vi.mock('googleapis', () => ({
    google: {
        auth: { GoogleAuth: vi.fn() },
        drive: vi.fn(() => ({})),
    },
}));

vi.mock('@opendataloader/pdf', () => ({
    convert: vi.fn(),
}));

import {
    classifyAppendixType,
    isAppendixSection,
    separateAppendixSections,
    type AppendixType,
} from '../pdfIndexing';

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

describe('classifyAppendixType', () => {

    // ----------------------------------------------------------
    // 수술분류표 → 'surgery_table'
    // ----------------------------------------------------------
    describe('수술분류 계열 → surgery_table', () => {
        it('"수술분류표" 포함 텍스트 → surgery_table', () => {
            const result: AppendixType = classifyAppendixType('[별표 1] 수술분류표\n수술코드 목록');
            expect(result).toBe('surgery_table');
        });

        it('"수술분류" 포함 텍스트 → surgery_table', () => {
            expect(classifyAppendixType('수술분류 기준에 따라 다음과 같이 분류합니다.')).toBe('surgery_table');
        });

        it('"수술코드" 포함 텍스트 → surgery_table', () => {
            expect(classifyAppendixType('수술코드 O001 ~ O999 해당 시 보험금 지급')).toBe('surgery_table');
        });

        it('"수술 분류" (띄어쓰기 포함) → surgery_table', () => {
            expect(classifyAppendixType('수술 분류 기준표')).toBe('surgery_table');
        });
    });

    // ----------------------------------------------------------
    // 장해분류표 → 'disability_table'
    // ----------------------------------------------------------
    describe('장해분류 계열 → disability_table', () => {
        it('"장해분류표" 포함 텍스트 → disability_table', () => {
            expect(classifyAppendixType('[별표 2] 장해분류표\n장해등급 1급~15급')).toBe('disability_table');
        });

        it('"장해분류" 포함 텍스트 → disability_table', () => {
            expect(classifyAppendixType('장해분류 기준에 따른 후유장해 판정')).toBe('disability_table');
        });

        it('"장해 분류" (띄어쓰기 포함) → disability_table', () => {
            expect(classifyAppendixType('장해 분류에 관한 별표입니다.')).toBe('disability_table');
        });

        it('"장해등급" 포함 텍스트 → disability_table', () => {
            expect(classifyAppendixType('장해등급 1급에 해당하는 경우 보험금 전액 지급')).toBe('disability_table');
        });
    });

    // ----------------------------------------------------------
    // 질병분류/ICD/KCD → 'disease_code'
    // ----------------------------------------------------------
    describe('질병분류/코드 계열 → disease_code', () => {
        it('"ICD 코드" 포함 텍스트 → disease_code', () => {
            expect(classifyAppendixType('ICD 코드 분류에 따른 질병 목록')).toBe('disease_code');
        });

        it('"ICD" 단독 → disease_code', () => {
            expect(classifyAppendixType('ICD-10 기준 질병코드 목록')).toBe('disease_code');
        });

        it('"KCD" 포함 텍스트 → disease_code', () => {
            expect(classifyAppendixType('KCD 한국표준질병사인분류 기준')).toBe('disease_code');
        });

        it('"질병분류" 포함 텍스트 → disease_code', () => {
            expect(classifyAppendixType('질병분류 코드 목록')).toBe('disease_code');
        });

        it('"질병코드" 포함 텍스트 → disease_code', () => {
            expect(classifyAppendixType('질병코드 C00~C97 악성신생물 해당')).toBe('disease_code');
        });

        it('"질병 분류" (띄어쓰기 포함) → disease_code', () => {
            expect(classifyAppendixType('질병 분류 기준 별표')).toBe('disease_code');
        });
    });

    // ----------------------------------------------------------
    // 그 외 → 'other'
    // ----------------------------------------------------------
    describe('해당 키워드 없음 → other', () => {
        it('"기타 별표" 텍스트 → other', () => {
            expect(classifyAppendixType('기타 별표 내용입니다.')).toBe('other');
        });

        it('빈 문자열 → other', () => {
            expect(classifyAppendixType('')).toBe('other');
        });

        it('보험료 관련 텍스트 → other', () => {
            expect(classifyAppendixType('보험료 산출 기준에 관한 부속서류')).toBe('other');
        });

        it('일반 약관 텍스트 → other', () => {
            expect(classifyAppendixType('이 약관은 피보험자의 생명에 관한 보험을 규정합니다.')).toBe('other');
        });

        // 우선순위: 수술분류가 장해분류보다 먼저 매칭됨
        it('수술분류와 장해분류 모두 포함 시 수술분류(surgery_table) 우선', () => {
            const result = classifyAppendixType('수술분류 및 장해분류 기준');
            expect(result).toBe('surgery_table');
        });
    });
});

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

describe('isAppendixSection', () => {

    // ----------------------------------------------------------
    // 긍정 케이스: 별표/부표/부속서류 패턴이 줄 시작에 있음
    // ----------------------------------------------------------
    describe('별표/부표/부속서류 패턴 → true', () => {
        it('"[별표 1]" 형식 → true', () => {
            expect(isAppendixSection('[별표 1] 수술분류표')).toBe(true);
        });

        it('"별표 수술분류표" (접두어 없이) → true', () => {
            expect(isAppendixSection('별표 수술분류표\n수술코드 목록입니다.')).toBe(true);
        });

        it('"별표1" (숫자 붙음) → true', () => {
            expect(isAppendixSection('별표1 장해분류 기준')).toBe(true);
        });

        it('"별표" 단독 → true', () => {
            expect(isAppendixSection('별표')).toBe(true);
        });

        it('"부표 1" → true', () => {
            expect(isAppendixSection('부표 1\n이하 내용')).toBe(true);
        });

        it('"부표" 단독 → true', () => {
            expect(isAppendixSection('부표')).toBe(true);
        });

        it('"부속서류" → true', () => {
            expect(isAppendixSection('부속서류\n첨부 내용')).toBe(true);
        });

        it('앞에 공백이 있는 "[별표 2]" → true (공백 후 패턴)', () => {
            // APPENDIX_PATTERNS: /^[\s]*\[?별표[\s]*\d*\]?/m
            expect(isAppendixSection('  [별표 2] 질병분류표')).toBe(true);
        });

        it('여러 줄 중 두 번째 줄에 "별표"가 있는 경우 → true (multiline)', () => {
            const text = '[PAGE 5]\n별표 장해분류표\n이하 내용';
            expect(isAppendixSection(text)).toBe(true);
        });

        it('"[별표]" (번호 없음) → true', () => {
            expect(isAppendixSection('[별표] 기타 첨부')).toBe(true);
        });
    });

    // ----------------------------------------------------------
    // 부정 케이스: 일반 조항 텍스트
    // ----------------------------------------------------------
    describe('일반 조항 텍스트 → false', () => {
        it('"제1조 (목적)" → false', () => {
            expect(isAppendixSection('제1조 (목적)\n이 약관은...')).toBe(false);
        });

        it('문장 중간에 "별표에 따라" 포함 (줄 시작이 아님) → false', () => {
            // "본 별표에 따라"는 줄 맨 앞이 아니므로 패턴 불매칭
            expect(isAppendixSection('보험금은 본 별표에 따라 지급됩니다.')).toBe(false);
        });

        it('일반 보험 약관 본문 → false', () => {
            expect(isAppendixSection('피보험자가 사망한 경우 보험수익자에게 보험금을 지급합니다.')).toBe(false);
        });

        it('빈 문자열 → false', () => {
            expect(isAppendixSection('')).toBe(false);
        });

        it('"부록" (별표/부표/부속서류 아님) → false', () => {
            // "부록"은 APPENDIX_PATTERNS에 없으므로 false
            expect(isAppendixSection('부록 A: 용어 설명')).toBe(false);
        });
    });
});

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

describe('separateAppendixSections', () => {

    // ----------------------------------------------------------
    // 별표 없는 문서
    // ----------------------------------------------------------
    describe('별표 없는 문서', () => {
        it('별표 섹션이 없으면 appendixSections는 빈 배열', () => {
            const text = [
                '[PAGE 1]',
                '제1조 (목적)',
                '이 약관은 피보험자의 생명에 관한 보험을 규정합니다.',
                '',
                '[PAGE 2]',
                '제2조 (보험금 지급)',
                '보험회사는 보험사고 발생 시 보험금을 지급합니다.',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(0);
            expect(mainText).toContain('제1조');
            expect(mainText).toContain('제2조');
        });

        it('빈 문자열 입력 → mainText 빈 문자열, appendixSections 빈 배열', () => {
            const { mainText, appendixSections } = separateAppendixSections('');
            expect(mainText).toBe('');
            expect(appendixSections).toHaveLength(0);
        });
    });

    // ----------------------------------------------------------
    // 별표 1개
    // ----------------------------------------------------------
    describe('별표 1개 포함 문서', () => {
        it('본문 + 별표 1개 → mainText에 본문, appendixSections에 별표 1개', () => {
            const text = [
                '[PAGE 1]',
                '제1조 (목적)',
                '이 약관은 피보험자의 생명에 관한 보험사고 발생 시 보험금을 지급합니다.',
                '',
                '[PAGE 2]',
                '별표 수술분류표',
                '다음 수술코드에 해당하는 경우 보험금을 지급합니다.',
                'O001 제왕절개',
                'O002 맹장수술',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(1);
            expect(mainText).toContain('제1조');
            expect(mainText).not.toContain('수술분류표');
            expect(appendixSections[0].text).toContain('수술분류표');
            expect(appendixSections[0].type).toBe('surgery_table');
        });

        it('별표 1개의 type이 classifyAppendixType 결과와 일치', () => {
            const text = [
                '[PAGE 1]',
                '제1조 이 약관은 장해보험에 관한 사항을 규정합니다.',
                '',
                '[PAGE 2]',
                '별표 장해분류표',
                '장해등급 기준: 1급 ~ 14급',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(1);
            expect(appendixSections[0].type).toBe('disability_table');
        });
    });

    // ----------------------------------------------------------
    // 별표 여러 개
    // ----------------------------------------------------------
    describe('별표 여러 개 포함 문서', () => {
        it('별표 2개 → appendixSections에 2개 각각 분리', () => {
            const text = [
                '[PAGE 1]',
                '제1조 (목적) 이 약관은 보험사고 발생 시 보험금을 지급합니다.',
                '',
                '[PAGE 2]',
                '[별표 1] 수술분류표',
                '수술코드 O001 ~ O999 목록',
                '',
                '[PAGE 3]',
                '[별표 2] 장해분류표',
                '장해등급 1급 ~ 14급 기준',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(2);
            expect(appendixSections[0].text).toContain('수술분류표');
            expect(appendixSections[0].type).toBe('surgery_table');
            expect(appendixSections[1].text).toContain('장해분류표');
            expect(appendixSections[1].type).toBe('disability_table');
        });

        it('별표 3개 → appendixSections에 3개 분리', () => {
            const text = [
                '[PAGE 1]',
                '제1조 이 약관은 종합보험에 관한 사항을 규정합니다.',
                '',
                '[PAGE 2]',
                '별표 수술분류표',
                '수술코드 목록',
                '',
                '[PAGE 3]',
                '별표 장해분류표',
                '장해등급 목록',
                '',
                '[PAGE 4]',
                '별표 질병분류표',
                'ICD-10 코드 목록',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(3);
            expect(appendixSections[0].type).toBe('surgery_table');
            expect(appendixSections[1].type).toBe('disability_table');
            expect(appendixSections[2].type).toBe('disease_code');
        });

        it('별표들이 mainText를 포함하지 않음', () => {
            const text = [
                '[PAGE 1]',
                '제1조 본문 내용입니다.',
                '',
                '[PAGE 2]',
                '[별표 1] 수술분류표',
                '수술코드 목록',
                '',
                '[PAGE 3]',
                '[별표 2] 장해분류표',
                '장해등급 기준',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            expect(mainText).toContain('제1조');
            expect(mainText).not.toContain('수술분류표');
            expect(mainText).not.toContain('장해분류표');
            expect(appendixSections).toHaveLength(2);
        });
    });

    // ----------------------------------------------------------
    // 별표 후 제N조 등장 → 본문으로 복귀
    // ----------------------------------------------------------
    describe('별표 후 제N조 등장 시 본문 복귀', () => {
        it('별표 섹션 이후 "제N조" 패턴이 등장하면 본문으로 복귀', () => {
            // separateAppendixSections는 [PAGE N] 단위로 처리
            // 별표 블록 내에서 \n제N조 패턴이 등장하면 본문으로 복귀
            const text = [
                '[PAGE 1]',
                '제1조 (목적) 이 약관은 보험을 규정합니다.',
                '',
                '[PAGE 2]',
                '별표 수술분류표',
                '수술코드 목록입니다.',
                '',
                '[PAGE 3]',
                // 이 블록에 \n제N조 패턴 포함 → 본문 복귀 트리거
                '\n제3조 (보험금 지급)',
                '보험사고 발생 시 보험금을 지급합니다.',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            // 별표 1개가 확정되고 제3조 블록은 mainText에 포함
            expect(appendixSections).toHaveLength(1);
            expect(appendixSections[0].text).toContain('수술분류표');
            expect(mainText).toContain('제3조');
        });

        it('본문 복귀 후 텍스트가 mainText에 올바르게 포함됨', () => {
            const text = [
                '[PAGE 1]',
                '제1조 (목적) 이 약관은 보험을 목적으로 합니다.',
                '',
                '[PAGE 2]',
                '별표 기타첨부',
                '첨부 내용입니다.',
                '',
                '[PAGE 3]',
                '\n제2조 (정의)',
                '이 약관에서 사용하는 용어를 정의합니다.',
                '',
                '[PAGE 4]',
                '제3조 (보험기간)',
                '보험기간은 보험증권에 기재된 기간입니다.',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(1);
            // 제2조는 별표 후 제N조 패턴 → 본문 복귀
            expect(mainText).toContain('제2조');
            expect(mainText).toContain('제3조');
            // mainText에는 별표 내용 미포함
            expect(mainText).not.toContain('기타첨부');
        });

        it('별표 섹션 내 여러 블록 축적 후 제N조로 복귀', () => {
            // 별표 시작 → 후속 블록들이 같은 appendix에 누적 → 제N조로 복귀
            const text = [
                '[PAGE 1]',
                '제1조 (목적) 보험사항을 규정합니다.',
                '',
                '[PAGE 2]',
                '[별표 1] 수술분류표',
                '수술 1: 제왕절개',
                '',
                '[PAGE 3]',
                '수술 2: 맹장수술',
                '수술 3: 담낭제거술',
                '',
                '[PAGE 4]',
                '\n제5조 (계약 해지)',
                '보험계약자는 언제든지 계약을 해지할 수 있습니다.',
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            // 별표 섹션은 [PAGE 2] + [PAGE 3] 내용 포함
            expect(appendixSections).toHaveLength(1);
            expect(appendixSections[0].text).toContain('[PAGE 2]');
            expect(appendixSections[0].text).toContain('맹장수술');
            // [PAGE 4]는 본문으로 복귀
            expect(mainText).toContain('제5조');
        });
    });

    // ----------------------------------------------------------
    // type 분류 정확성
    // ----------------------------------------------------------
    describe('appendixSections type 정확성', () => {
        it('KCD 코드 포함 별표 → disease_code', () => {
            const text = [
                '[PAGE 1]',
                '제1조 이 약관은 질병보험을 규정합니다.',
                '',
                '[PAGE 2]',
                '별표 KCD 질병분류코드',
                'KCD-10 기준 질병코드 목록',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(1);
            expect(appendixSections[0].type).toBe('disease_code');
        });

        it('분류 키워드 없는 별표 → other', () => {
            const text = [
                '[PAGE 1]',
                '제1조 이 약관은 종합보험을 규정합니다.',
                '',
                '[PAGE 2]',
                '부속서류',
                '보험청구 관련 서류 목록입니다.',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections).toHaveLength(1);
            expect(appendixSections[0].type).toBe('other');
        });
    });

    // ----------------------------------------------------------
    // mainText와 appendixSections 합산 내용 무결성
    // ----------------------------------------------------------
    describe('분리 결과 내용 무결성', () => {
        it('mainText + appendixSections.text 합산이 원본 내용을 모두 포함', () => {
            const mainContent = '제1조 (목적) 이 약관은 보험을 규정합니다.';
            const appendixContent = '별표 수술분류표\n수술코드 목록';

            const text = [
                `[PAGE 1]\n${mainContent}`,
                `[PAGE 2]\n${appendixContent}`,
            ].join('\n');

            const { mainText, appendixSections } = separateAppendixSections(text);

            const allText = mainText + appendixSections.map(s => s.text).join('');

            expect(allText).toContain('제1조');
            expect(allText).toContain('수술분류표');
        });

        it('appendixSections의 text 속성은 별표 블록 내용을 포함', () => {
            const text = [
                '[PAGE 1]',
                '제1조 본문입니다.',
                '',
                '[PAGE 2]',
                '[별표 3] 장해분류표',
                '장해등급 1급: 식물인간 상태',
            ].join('\n');

            const { appendixSections } = separateAppendixSections(text);

            expect(appendixSections[0].text).toContain('[별표 3]');
            expect(appendixSections[0].text).toContain('장해등급 1급');
        });
    });
});
