/**
 * Research Agent / Skill Score / Success Pattern DB (Self-Evolving OS Phase 6) 테스트.
 */
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { parseBrief, fallbackBrief, runResearch, formatProposalMarkdown } from '../src/intelligence/researchAgent';
import {
    computeSkillScores,
    formatSkillScoresMarkdown,
    isSuccessTurn,
    appendSuccessPattern,
    loadSuccessPatterns,
} from '../src/intelligence/skillScore';
import type { QueueItem } from '../src/intelligence/learningQueue';
import type { ReflectionRecord } from '../src/intelligence/reflectionStore';

const ITEM: QueueItem = {
    id: 'need-market-research', topic: '시장조사 역량 보강', priority: 60, reason: '근거 없는 수행 다수',
    status: 'approved', createdAt: 'a', updatedAt: 'a',
};

function mk(partial: Partial<ReflectionRecord>): ReflectionRecord {
    return {
        ts: '2026-06-11T10:00:00.000Z', taskId: 'meeting-minutes', taskLabel: '회의록',
        confidenceScore: 80, confidenceBand: 'medium', missing: [], escalated: false,
        criticIssues: null, promptPreview: '회의록 정리해줘', usedSources: ['회의기록.md'],
        ...partial,
    };
}

describe('researchAgent', () => {
    it('parseBrief — 잡설 섞인 JSON 파싱, 실패 시 fallback', () => {
        const ok = parseBrief('계획: {"questions":["q1","q2"],"keywords":["k"],"sourceTypes":["공식 문서"]} 끝');
        expect(ok!.questions).toEqual(['q1', 'q2']);
        expect(parseBrief('JSON 없음')).toBeNull();
        expect(fallbackBrief('주제').questions.length).toBeGreaterThan(0);
    });

    it('runResearch — 브리프→내부현황→초안→Validation 게이트 (출처 없음 = review)', async () => {
        const calls: string[] = [];
        const pkg = await runResearch({
            item: ITEM,
            fetchInternalRefs: async () => [{ title: '기존문서', content: '기존 시장조사 노트 내용', filePath: 'a.md' }],
            callLlm: async (system) => {
                calls.push(system.slice(0, 20));
                if (system.includes('조사 계획')) {
                    return '{"questions":["시장 규모 출처는?"],"keywords":["시장조사"],"sourceTypes":["공식 통계"]}';
                }
                return '## 시장 규모\n일반적으로 통계청 자료를 쓴다 (모델 지식 — 추정, 출처 확인 필요)';
            },
            nowIso: '2026-06-11T00:00:00.000Z',
        });
        expect(pkg.brief.questions[0]).toContain('시장 규모');
        expect(pkg.internalRefs.length).toBe(1);
        expect(pkg.draft).toContain('추정');
        // 출처가 없으므로 자동 수용 불가 — Permission Based Learning 게이트.
        expect(pkg.validation.verdict).toBe('review');
        expect(pkg.validation.checks.hasSource).toBe(false);
        expect(calls.length).toBe(2);
    });

    it('LLM 전부 실패해도 fallback 브리프로 패키지 생성', async () => {
        const pkg = await runResearch({
            item: ITEM,
            fetchInternalRefs: async () => [],
            callLlm: async () => { throw new Error('down'); },
            nowIso: '2026-06-11T00:00:00.000Z',
        });
        expect(pkg.brief.questions.length).toBeGreaterThan(0);
        expect(pkg.draft).toContain('실패');
    });

    it('formatProposalMarkdown — 판정·브리프·다음 단계 포함', async () => {
        const pkg = await runResearch({
            item: ITEM, fetchInternalRefs: async () => [],
            callLlm: async () => '{"questions":["q"],"keywords":["k"],"sourceTypes":["s"]}',
            nowIso: '2026-06-11T00:00:00.000Z',
        });
        const md = formatProposalMarkdown(pkg, { dateStr: 'now', modelName: 'gemma' });
        expect(md).toContain('검증 판정: review');
        expect(md).toContain('/research');
        expect(md).toContain('done 으로 변경');
    });
});

describe('skillScore', () => {
    it('확신도·충족률·비에스컬레이션 가중 합산 + 추세', () => {
        const records = [
            mk({ ts: '2026-06-01T10:00:00Z', confidenceScore: 50, missing: ['기한'], escalated: true }),
            mk({ ts: '2026-06-02T10:00:00Z', confidenceScore: 55, missing: ['기한'] }),
            mk({ ts: '2026-06-08T10:00:00Z', confidenceScore: 90, missing: [] }),
            mk({ ts: '2026-06-09T10:00:00Z', confidenceScore: 95, missing: [] }),
        ];
        const scores = computeSkillScores(records);
        expect(scores.length).toBe(1);
        expect(scores[0].trend).toBe('up');
        expect(scores[0].secondHalf).toBeGreaterThan(scores[0].firstHalf);
        const md = formatSkillScoresMarkdown(scores);
        expect(md).toContain('상승');
    });

    it('표본 4건 미만이면 추세 flat', () => {
        const scores = computeSkillScores([mk({}), mk({ confidenceScore: 20 })]);
        expect(scores[0].trend).toBe('flat');
    });

    it('isSuccessTurn — 전 요소 충족 + 확신도 90+ 만', () => {
        expect(isSuccessTurn(mk({ confidenceScore: 92, missing: [] }))).toBe(true);
        expect(isSuccessTurn(mk({ confidenceScore: 92, missing: ['기한'] }))).toBe(false);
        expect(isSuccessTurn(mk({ confidenceScore: 80, missing: [] }))).toBe(false);
    });

    it('append → load 성공 패턴 라운드트립 (성공 turn 만 저장)', () => {
        const brain = fs.mkdtempSync(path.join(os.tmpdir(), 'astra-test-sp-'));
        expect(appendSuccessPattern(brain, mk({ confidenceScore: 95, missing: [] }))).toBe(true);
        expect(appendSuccessPattern(brain, mk({ confidenceScore: 50 }))).toBe(false);
        const patterns = loadSuccessPatterns(brain);
        expect(patterns.length).toBe(1);
        expect(patterns[0].usedSources).toEqual(['회의기록.md']);
    });
});
