/**
 * Regression guard for the /meet prompt policy (v2.2.258).
 *
 * After a real meeting record review, the policy shifted to:
 *  1) STT speaker numbers ("참석자 N") must NEVER appear in output — speakers are
 *     normalized to team/role instead (individual names only when certain).
 *  2) empty cells are "—", not guessed placeholders; fully-undecided → open issue.
 *  3) 근거 is a timestamp [mm:ss], not a raw STT quote.
 *  4) decision vs action boundary: 일감→action table, 결정→pure direction only.
 *  5) rejected/withdrawn hypotheses must not be promoted to issues/decisions.
 *
 * Prompts can't be unit-tested for model behavior, but we CAN guard that the
 * policy text doesn't silently get dropped in a future refactor.
 */

import {
    buildMeetPrompt,
    buildMeetExtractPrompt,
    buildMeetReducePrompt,
} from '../src/features/datacollect/prompts/meetPrompt';

const transcript = '참석자 1 00:01 이거는 이렇게 이렇게 해주세요. 참석자 2 00:10 네 수정할게요.';
const metadata = '회의명: 테스트';

describe('/meet prompt — speaker normalization & slim format policy', () => {
    // The OUTPUT_FORMAT block is shared by the single-shot and reduce paths, so
    // both must carry the policy.
    const sharedFormatBuilders: Array<[string, string]> = [
        ['buildMeetPrompt', buildMeetPrompt(transcript, metadata)],
        ['buildMeetReducePrompt', buildMeetReducePrompt('## 액션\n- [넥서스개발팀] 작업', metadata)],
    ];

    test.each(sharedFormatBuilders)('%s bans "참석자 N" tokens in output', (_name, prompt) => {
        expect(prompt).toMatch(/"참석자 N"/);
        expect(prompt).toMatch(/토큰 0개|절대 금지|절대 쓰지/);
    });

    test.each(sharedFormatBuilders)('%s attributes speakers by team/role', (_name, prompt) => {
        expect(prompt).toMatch(/팀\/역할/);
    });

    test.each(sharedFormatBuilders)('%s requires timestamp 근거 ([mm:ss])', (_name, prompt) => {
        expect(prompt).toMatch(/\[mm:ss\]/);
    });

    test.each(sharedFormatBuilders)('%s separates decision from action', (_name, prompt) => {
        expect(prompt).toMatch(/결정 ↔ 액션 경계|순수 방향\/정책|결정\/액션 경계/);
    });

    test.each(sharedFormatBuilders)('%s blanks unknown cells with "—" instead of guessing', (_name, prompt) => {
        expect(prompt).toMatch(/빈 칸은 "—"/);
    });

    test('extract (map) stage normalizes speakers to role, tags dialectic state & timestamps', () => {
        const p = buildMeetExtractPrompt('참석자 1 00:01 이거 이렇게요', metadata, 1, 3);
        expect(p).toMatch(/팀\/역할/);          // role mapping
        expect(p).toMatch(/\[mm:ss\]/);          // timestamp grounding
        expect(p).toMatch(/반박됨|철회됨/);       // dialectic state tags
        expect(p).toMatch(/안건\/주제/);          // agenda coverage list
        expect(p).toContain('[내용 확인필요]');   // content-empty deictic guard retained
    });

    test('reduce stage extracts a global headline & dedups across chunks', () => {
        const p = buildMeetReducePrompt('## 액션\n- [기획] 작업', metadata);
        expect(p).toMatch(/전역 헤드라인/);
        expect(p).toMatch(/dedup|병합/);
        expect(p).toMatch(/반박됨|폐기/);        // rejected hypotheses not promoted
    });

    test('final checklist references the new gates', () => {
        const prompt = buildMeetPrompt(transcript, metadata);
        expect(prompt).toMatch(/"참석자 N" 토큰 0개/);
        expect(prompt).toMatch(/빈 칸은 "—"/);
    });
});
