import { describe, it, expect } from 'vitest';
import {
  getToolCategoryMap,
  listToolCategories,
  SENSITIVE_CATEGORIES,
  SENSITIVE_TOOLS,
  META_TOOLS,
} from './tool-categories.js';

describe('tool-categories', () => {
  it('maps core tools to core', async () => {
    const m = await getToolCategoryMap();
    expect(m.get('Read')).toBe('core');
    expect(m.get('Bash')).toBe('core');
    expect(m.get('Write')).toBe('core');
    expect(m.get('Edit')).toBe('core');
    expect(m.get('Glob')).toBe('core');
    expect(m.get('Grep')).toBe('core');
  });

  it('maps non-core tools to their category', async () => {
    const m = await getToolCategoryMap();
    expect(m.get('BrowseWeb')).toBe('browser');
    expect(m.get('WebSearch')).toBe('web');
    expect(m.get('SQLite')).toBe('data');
  });

  it('includes app-test category with TestWorkspaceApp', async () => {
    const m = await getToolCategoryMap();
    expect(m.get('TestWorkspaceApp')).toBe('app-test');
  });

  it('maps calendar tools to the calendar category', async () => {
    const m = await getToolCategoryMap();
    expect(m.get('AddCalendarEvent')).toBe('calendar');
    expect(m.get('ListCalendarEvents')).toBe('calendar');
  });

  it('lists categories including calendar (workspace-tool-policy default-on)', async () => {
    const cats = await listToolCategories();
    expect(cats).toContain('calendar');
    expect(SENSITIVE_CATEGORIES.has('calendar')).toBe(false);
  });

  it('lists categories including core and browser', async () => {
    const cats = await listToolCategories();
    expect(cats).toContain('core');
    expect(cats).toContain('browser');
    expect(cats).toContain('web');
    expect(cats).toContain('ssh');
    expect(cats).toContain('skills');
    expect(cats).toContain('app-test');
  });

  it('has no duplicate categories in listToolCategories', async () => {
    const cats = await listToolCategories();
    const unique = [...new Set(cats)];
    expect(cats).toEqual(unique);
  });

  it('marks ssh and browser as sensitive categories', () => {
    expect(SENSITIVE_CATEGORIES.has('ssh')).toBe(true);
    expect(SENSITIVE_CATEGORIES.has('browser')).toBe(true);
    expect(SENSITIVE_CATEGORIES.has('core')).toBe(false);
    expect(SENSITIVE_CATEGORIES.has('web')).toBe(false);
  });

  it('marks Bash as a sensitive tool', () => {
    expect(SENSITIVE_TOOLS.has('Bash')).toBe(true);
    expect(SENSITIVE_TOOLS.has('Read')).toBe(false);
  });

  it('marks SpawnSubTask as a sensitive (opt-in) tool but not WaitSubTask', () => {
    // D1: SpawnSubTask is opt-in (parallel separate-job decomposition). delegate is
    // the default-on serial path. WaitSubTask stays default-on (no-op without children).
    expect(SENSITIVE_TOOLS.has('SpawnSubTask')).toBe(true);
    expect(SENSITIVE_TOOLS.has('WaitSubTask')).toBe(false);
    expect(SENSITIVE_TOOLS.has('delegate')).toBe(false);
  });

  it('exports META_TOOLS with expected entries', () => {
    expect(META_TOOLS).toBeInstanceOf(Set);
    expect(META_TOOLS.has('ReadToolDoc')).toBe(true);
    expect(META_TOOLS.has('ReadSkill')).toBe(true);
    expect(META_TOOLS.has('CreateChecklist')).toBe(true);
  });

  it('surfaces RequestTool in the catalog and tags it as a meta tool', async () => {
    // RequestTool lives in tool-request.ts; it must be in MODULE_SPECS so the
    // /api/tools catalog (loadBuiltinEntries) loads its def, and in META_TOOLS
    // so it is tagged source:'meta' like its sibling always-on tools.
    const m = await getToolCategoryMap();
    expect(m.get('RequestTool')).toBe('tool-request');
    expect(META_TOOLS.has('RequestTool')).toBe(true);
  });

  it('applies first-write-wins: core tools not overridden by later modules', async () => {
    // If a tool appears in both core and another module, core wins
    const m = await getToolCategoryMap();
    // Bash is core — must stay 'core' even if any other module listed it
    expect(m.get('Bash')).toBe('core');
  });
});
