import { describe, expect, it } from 'vitest';
import { buildClassificationPrompt, parseClassificationResponse } from './piece-classifier.js';

describe('buildClassificationPrompt', () => {
  it('includes task text and piece descriptions', () => {
    const prompt = buildClassificationPrompt(
      'Rustの最新動向を調査して',
      [
        { name: 'research', description: '調査・分析タスク' },
        { name: 'general', description: '汎用タスク' },
      ],
      [],
    );
    expect(prompt).toContain('Rustの最新動向を調査して');
    expect(prompt).toContain('research');
    expect(prompt).toContain('general');
  });

  it('includes file names when provided', () => {
    const prompt = buildClassificationPrompt(
      '集計して',
      [{ name: 'office-process', description: 'Office処理' }],
      ['sales.xlsx'],
    );
    expect(prompt).toContain('sales.xlsx');
  });

  it('biases the classifier toward chat as the default piece', () => {
    const prompt = buildClassificationPrompt(
      '何かの質問',
      [
        { name: 'chat', description: '汎用デフォルト' },
        { name: 'slide', description: 'スライド作成' },
      ],
      [],
    );
    expect(prompt).toContain('デフォルトは "chat"');
    expect(prompt).toContain('迷ったら "chat" を選ぶ');
  });

  it('routes workspace-app requests to workspace-app, not chat/piece-builder', () => {
    const prompt = buildClassificationPrompt(
      'ワークスペースアプリを作って',
      [
        { name: 'chat', description: '汎用デフォルト' },
        { name: 'workspace-app', description: 'ワークスペースアプリ作成', keywords: ['ワークスペースアプリ', 'アプリを作'] },
        { name: 'piece-builder', description: 'Piece 定義作成' },
      ],
      [],
    );
    // The selection rule names workspace-app and clarifies it is NOT "just code generation".
    expect(prompt).toContain('workspace-app');
    expect(prompt).toContain('apps/{name}/index.html');
    // The keyword hint fires for the user's phrasing.
    expect(prompt).toContain('ワークスペースアプリ');
  });

  it('distinguishes a one-by-one timeline deep-dive sweep (sns-deep-sweep) from single-topic SNS research (sns-research)', () => {
    const prompt = buildClassificationPrompt(
      'タイムラインのツイートを1件ずつまとめて深掘りして統合して',
      [
        { name: 'chat', description: '汎用デフォルト' },
        { name: 'sns-research', description: 'SNS の声をトピック単位で調べる' },
        { name: 'sns-deep-sweep', description: '多数の投稿を1件ずつ個別に深掘りして統合' },
      ],
      [],
    );
    // These strings live only in the selection-rule bullets, not in the 選択肢 piece list,
    // so the test goes red if the rule lines are removed (genuine regression guard).
    expect(prompt).toContain('→ sns-deep-sweep');
    expect(prompt).toContain('→ sns-research');
    expect(prompt).toContain('N件をそれぞれ詳しく');
  });
});

describe('parseClassificationResponse', () => {
  const validPieces = ['research', 'general', 'data-process'];

  it('extracts valid piece name from response', () => {
    expect(parseClassificationResponse('research', validPieces)).toBe('research');
  });

  it('extracts piece name from noisy response', () => {
    expect(parseClassificationResponse('最適なピースは research です', validPieces)).toBe('research');
  });

  it('prefers longer match over shorter (data-process over general)', () => {
    expect(parseClassificationResponse('data-process が最適', validPieces)).toBe('data-process');
  });

  it('returns null for invalid piece', () => {
    expect(parseClassificationResponse('unknown-piece', validPieces)).toBeNull();
  });

  it('returns null for empty response', () => {
    expect(parseClassificationResponse('', validPieces)).toBeNull();
  });
});
