import { describe, it, expect } from 'vitest';
import type { ToolCall } from '../../llm/openai-compat.js';
import { extractInvalidationTrigger } from './invalidation.js';

function call(name: string, args: unknown): ToolCall {
  return {
    id: 'tc-1',
    type: 'function',
    function: {
      name,
      arguments: typeof args === 'string' ? args : JSON.stringify(args),
    },
  };
}

describe('extractInvalidationTrigger', () => {
  it('invalidates the edited path for Edit and Write', () => {
    expect(extractInvalidationTrigger(call('Edit', { file_path: '/ws/a.ts' })))
      .toEqual({ kind: 'path', path: '/ws/a.ts' });
    expect(extractInvalidationTrigger(call('Write', { file_path: 'out/b.md' })))
      .toEqual({ kind: 'path', path: 'out/b.md' });
  });

  it('falls back to all_files when Edit args have no usable file_path', () => {
    expect(extractInvalidationTrigger(call('Edit', {}))).toEqual({ kind: 'all_files' });
    expect(extractInvalidationTrigger(call('Edit', { file_path: '' }))).toEqual({ kind: 'all_files' });
    expect(extractInvalidationTrigger(call('Edit', { file_path: 42 }))).toEqual({ kind: 'all_files' });
  });

  it('falls back to all_files when Edit args are unparseable JSON', () => {
    expect(extractInvalidationTrigger(call('Write', '{not json'))).toEqual({ kind: 'all_files' });
  });

  it('invalidates everything for Bash regardless of args', () => {
    expect(extractInvalidationTrigger(call('Bash', { command: 'ls' }))).toEqual({ kind: 'all_files' });
    expect(extractInvalidationTrigger(call('Bash', '{broken'))).toEqual({ kind: 'all_files' });
  });

  it('produces no trigger for read-only and unknown tools', () => {
    for (const name of ['Read', 'Grep', 'Glob', 'WebFetch', 'NoSuchTool']) {
      expect(extractInvalidationTrigger(call(name, { file_path: '/ws/a.ts' }))).toBeNull();
    }
  });
});
