import {
    detectLessonKind,
    extractPreventionChecklist,
    buildLessonChecklistBlock,
    lessonTemplate,
    lessonSlug,
    LESSON_DIR_RE,
    isQaRegressionFeedback,
    parseLessonFrontmatter,
    normalizeLessonTitle,
    bumpLessonOccurrences,
    findUnaddressedChecklistItems,
} from '../src/retrieval/lessonHelpers';

describe('lessonHelpers.detectLessonKind', () => {
    it('detects by path segment', () => {
        expect(detectLessonKind('lessons/2026-05-12-foo.md', '# Foo')).toBe('lesson');
        expect(detectLessonKind('docs/playbooks/release.md', 'stuff')).toBe('playbook');
        expect(detectLessonKind('qa-findings/bug-x.md', 'stuff')).toBe('qa-finding');
        expect(detectLessonKind('a/b/lesson/x.md', '')).toBe('lesson'); // singular "lesson"
        expect(detectLessonKind('notes/architecture.md', '# Arch')).toBe('');
    });
    it('frontmatter type wins over path', () => {
        const fm = '---\ntype: qa-finding\ntitle: x\n---\n# body';
        expect(detectLessonKind('notes/whatever.md', fm)).toBe('qa-finding');
        const fm2 = '---\ntype: "lesson"\n---\nbody';
        expect(detectLessonKind('random.md', fm2)).toBe('lesson');
    });
    it('ignores non-lesson frontmatter / no frontmatter', () => {
        expect(detectLessonKind('notes/x.md', '---\ntype: note\n---\nbody')).toBe('');
        expect(detectLessonKind('notes/x.md', 'just text, no frontmatter')).toBe('');
        expect(detectLessonKind('notes/x.md', '')).toBe('');
    });
    it('LESSON_DIR_RE matches expected dirs only', () => {
        expect(LESSON_DIR_RE.test('lessons/x.md')).toBe(true);
        expect(LESSON_DIR_RE.test('a\\playbooks\\y.md')).toBe(true);
        expect(LESSON_DIR_RE.test('qa_findings/z.md')).toBe(true);
        expect(LESSON_DIR_RE.test('lessons-archive/x.md')).toBe(false); // not a path segment boundary
        expect(LESSON_DIR_RE.test('records/dev.md')).toBe(false);
    });
});

describe('lessonHelpers.extractPreventionChecklist', () => {
    it('pulls the bullets under a Prevention Checklist heading', () => {
        const card = [
            '# Lesson: X',
            '## Root Cause',
            'because reasons',
            '## Prevention Checklist',
            '- check the allowlist',
            '* approval flow exists',
            '- path boundary validated',
            '## Applies To',
            '- security',
        ].join('\n');
        expect(extractPreventionChecklist(card)).toEqual([
            'check the allowlist', 'approval flow exists', 'path boundary validated',
        ]);
    });
    it('returns [] when there is no checklist', () => {
        expect(extractPreventionChecklist('# Lesson\nsome text')).toEqual([]);
        expect(extractPreventionChecklist('')).toEqual([]);
    });
});

describe('lessonHelpers.buildLessonChecklistBlock', () => {
    it('renders an ACTIVE LESSONS block with checklists when available', () => {
        const block = buildLessonChecklistBlock([
            { title: 'lessons/telegram.md', content: '# X\n## Prevention Checklist\n- allowlist required\n- approval flow' },
            { title: 'lessons/paths.md', content: 'no checklist here, just prose about path safety' },
        ]);
        expect(block).toContain('ACTIVE LESSONS');
        expect(block).toContain('### lessons/telegram.md');
        expect(block).toContain('- [ ] allowlist required');
        expect(block).toContain('- [ ] approval flow');
        expect(block).toContain('### lessons/paths.md');
        expect(block).toContain('just prose about path safety');
        expect(block).toContain('END ACTIVE LESSONS');
    });
    it('returns empty string for no chunks', () => {
        expect(buildLessonChecklistBlock([])).toBe('');
    });
});

describe('lessonHelpers.lessonTemplate / lessonSlug', () => {
    it('template has frontmatter + the standard sections', () => {
        const t = lessonTemplate('Telegram 원격 실행은 allowlist 필수', '2026-05-12');
        expect(t).toMatch(/^---\ntype: lesson\n/);
        expect(t).toContain('title: Telegram 원격 실행은 allowlist 필수');
        expect(t).toContain('last-seen: 2026-05-12');
        for (const h of ['## Situation', '## Mistake / Risk', '## Root Cause', '## Fix', '## Prevention Checklist', '## Applies To']) {
            expect(t).toContain(h);
        }
        // detectLessonKind should recognize the generated template
        expect(detectLessonKind('lessons/x.md', t)).toBe('lesson');
    });
    it('slug is filesystem-safe and length-bounded', () => {
        expect(lessonSlug('Hello, World! / weird:chars')).toBe('hello-world-weird-chars');
        expect(lessonSlug('한글 제목 테스트')).toBe('한글-제목-테스트');
        expect(lessonSlug('')).toBe('lesson');
        expect(lessonSlug('a'.repeat(200)).length).toBeLessThanOrEqual(60);
    });
});

describe('lessonHelpers.isQaRegressionFeedback', () => {
    it('flags recurring-problem complaints (ko/en)', () => {
        expect(isQaRegressionFeedback('이거 또 안 돼')).toBe(true);
        expect(isQaRegressionFeedback('아까랑 비슷한 실수네')).toBe(true);
        expect(isQaRegressionFeedback('왜 자꾸 이래?')).toBe(true);
        expect(isQaRegressionFeedback('고쳤는데 또 깨졌어')).toBe(true);
        expect(isQaRegressionFeedback('여전히 안 돼')).toBe(true);
        expect(isQaRegressionFeedback('this is a regression')).toBe(true);
        expect(isQaRegressionFeedback('why does this keep failing again')).toBe(true);
    });
    it('does not flag ordinary requests', () => {
        expect(isQaRegressionFeedback('이 함수 리팩터링해줘')).toBe(false);
        expect(isQaRegressionFeedback('add a new endpoint for users')).toBe(false);
        expect(isQaRegressionFeedback('')).toBe(false);
        expect(isQaRegressionFeedback('또')).toBe(false); // too short / not a complaint
    });
});

describe('lessonHelpers.parseLessonFrontmatter / normalizeLessonTitle / bumpLessonOccurrences', () => {
    const card = [
        '---',
        'type: lesson',
        'title: Telegram remote exec needs allowlist',
        'applies-to: [telegram, "remote-execution", security]',
        'occurrences: 2',
        'last-seen: 2026-05-01',
        '---',
        '# body',
        'stuff',
    ].join('\n');
    it('parses frontmatter fields', () => {
        const fm = parseLessonFrontmatter(card);
        expect(fm.type).toBe('lesson');
        expect(fm.title).toBe('Telegram remote exec needs allowlist');
        expect(fm.occurrences).toBe(2);
        expect(fm.appliesTo).toEqual(['telegram', 'remote-execution', 'security']);
    });
    it('returns {} when there is no frontmatter', () => {
        expect(parseLessonFrontmatter('# just a heading\ntext')).toEqual({});
        expect(parseLessonFrontmatter('')).toEqual({});
    });
    it('normalizeLessonTitle strips punctuation/case/space for matching', () => {
        expect(normalizeLessonTitle('Telegram 원격 실행은 allowlist 필수!')).toBe(normalizeLessonTitle('telegram원격실행은allowlist필수'));
        expect(normalizeLessonTitle('A B  C')).toBe('abc');
    });
    it('bumpLessonOccurrences increments and updates last-seen', () => {
        const out = bumpLessonOccurrences(card, '2026-05-12');
        expect(parseLessonFrontmatter(out).occurrences).toBe(3);
        expect(out).toContain('last-seen: 2026-05-12');
        expect(out).toContain('# body'); // body untouched
    });
    it('bumpLessonOccurrences inserts the keys when missing', () => {
        const minimal = '---\ntype: lesson\ntitle: X\n---\nbody';
        const out = bumpLessonOccurrences(minimal, '2026-05-12');
        expect(parseLessonFrontmatter(out).occurrences).toBe(2);
        expect(out).toContain('last-seen: 2026-05-12');
    });
    it('bumpLessonOccurrences leaves a frontmatter-less file unchanged', () => {
        expect(bumpLessonOccurrences('no frontmatter here', '2026-05-12')).toBe('no frontmatter here');
    });
});

describe('lessonHelpers.findUnaddressedChecklistItems', () => {
    const card = [
        '# Lesson',
        '## Prevention Checklist',
        '- 원격 실행 기능은 allowlist 필수인지 확인한다',
        '- 파일 변경은 승인 흐름을 거치는지 확인한다',
        '- <템플릿 placeholder 항목>',
        '## Applies To',
        '- security',
    ].join('\n');
    it('flags items whose terms do not appear in the answer', () => {
        const answer = '이번 변경은 승인 흐름을 거치도록 했습니다. 파일 변경 시 사용자에게 확인을 받습니다.';
        const out = findUnaddressedChecklistItems(answer, [card]);
        // "allowlist" item is unaddressed; the "승인 흐름/파일 변경" item is addressed; the placeholder is skipped
        expect(out).toEqual(['원격 실행 기능은 allowlist 필수인지 확인한다']);
    });
    it('returns [] when the answer touches all checklist items', () => {
        const answer = 'allowlist 정책을 추가했고, 파일 변경은 승인 흐름을 거칩니다.';
        expect(findUnaddressedChecklistItems(answer, [card])).toEqual([]);
    });
    it('returns [] with no answer or no lessons', () => {
        expect(findUnaddressedChecklistItems('', [card])).toEqual([]);
        expect(findUnaddressedChecklistItems('something', [])).toEqual([]);
    });
});
