import { describe, test, expect, vi, beforeEach } from 'vitest';
import { mockFs, MockAIProvider, setupMocks } from './utils';

// Setup mocks before imports
setupMocks();

// Import after mocks are setup
import { Oneshot } from '../src/oneshot';
import { ToolError } from '../src/errors';

describe('Oneshot', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockFs.existsSync.mockReturnValue(true);
  });

  test('sends basic prompt to AI provider', async () => {
    const mockResponse = 'AI response';
    const provider = new MockAIProvider(mockResponse);
    mockFs.readFileSync.mockReturnValue('test prompt');

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md'
    }, provider);

    const results = await oneshot.process();

    expect(results).toHaveLength(1);
    expect(results[0].response).toBe(mockResponse);
    expect(results[0].variation).toBeUndefined();
    expect(results[0].systemPrompt).toBe('');
  });

  test('includes system prompt when provided', async () => {
    const provider = new MockAIProvider();
    mockFs.readFileSync
      .mockReturnValueOnce('user prompt')  // promptFile
      .mockReturnValueOnce('system prompt'); // systemFile

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md',
      systemFile: 'system.md'
    }, provider);

    const results = await oneshot.process();

    expect(results[0].systemPrompt).toBe('system prompt');
  });

  test('handles variations', async () => {
    const provider = new MockAIProvider();
    mockFs.readFileSync.mockReturnValue('base prompt');

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md',
      variations: ['var1', 'var2']
    }, provider);

    const results = await oneshot.process();

    expect(results).toHaveLength(2);
    expect(results[0].variation).toBe('var1');
    expect(results[1].variation).toBe('var2');
  });

  test('handles multiple iterations', async () => {
    const provider = new MockAIProvider();
    mockFs.readFileSync.mockReturnValue('test prompt');

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md',
      iterations: 3
    }, provider);

    const results = await oneshot.process();

    expect(results).toHaveLength(3);
  });

  test('handles variations with iterations', async () => {
    const provider = new MockAIProvider();
    mockFs.readFileSync.mockReturnValue('test prompt');

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md',
      variations: ['var1', 'var2'],
      iterations: 2
    }, provider);

    const results = await oneshot.process();

    expect(results).toHaveLength(4); // 2 variations × 2 iterations
    expect(results.filter(r => r.variation === 'var1')).toHaveLength(2);
    expect(results.filter(r => r.variation === 'var2')).toHaveLength(2);
  });

  test('throws error if prompt file does not exist', async () => {
    mockFs.existsSync.mockReturnValue(false);
    const provider = new MockAIProvider();

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'nonexistent.md'
    }, provider);

    await expect(oneshot.process()).rejects.toThrow(ToolError);
  });

  test('throws error if system file does not exist', async () => {
    mockFs.existsSync
      .mockReturnValueOnce(true)  // promptFile exists
      .mockReturnValueOnce(false); // systemFile doesn't exist
    const provider = new MockAIProvider();

    const oneshot = new Oneshot({
      model: 'test-model',
      promptFile: 'prompt.md',
      systemFile: 'nonexistent.md'
    }, provider);

    await expect(oneshot.process()).rejects.toThrow(ToolError);
  });
}); 