import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AnthropicClient } from './anthropic.js';

// Shared mock function so we can override per-test
const mockCreate = vi.fn().mockResolvedValue({
  content: [{ type: 'text', text: 'Hello from Claude!' }],
  stop_reason: 'end_turn',
  usage: { input_tokens: 10, output_tokens: 5 },
});

const mockStream = vi.fn().mockReturnValue({
  [Symbol.asyncIterator]: async function* () {
    yield { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Hello ' } };
    yield { type: 'content_block_delta', delta: { type: 'text_delta', text: 'from ' } };
    yield { type: 'content_block_delta', delta: { type: 'text_delta', text: 'Claude!' } };
  },
  finalMessage: vi.fn().mockResolvedValue({
    usage: { input_tokens: 10, output_tokens: 5 },
  }),
});

vi.mock('@anthropic-ai/sdk', () => ({
  default: vi.fn().mockImplementation(() => ({
    messages: {
      create: mockCreate,
      stream: mockStream,
    },
  })),
}));

describe('AnthropicClient', () => {
  it('sends messages and returns response', async () => {
    const client = new AnthropicClient({
      apiKey: 'test-key',
      model: 'claude-sonnet-4-20250514',
    });

    const response = await client.chat({
      messages: [{ role: 'user', content: 'Hello' }],
    });

    expect(response.content).toBe('Hello from Claude!');
    expect(response.stopReason).toBe('end_turn');
    expect(response.usage.inputTokens).toBe(10);
    expect(response.usage.outputTokens).toBe(5);
  });
});

describe('AnthropicClient streaming', () => {
  it('streams messages chunk by chunk', async () => {
    const client = new AnthropicClient({
      apiKey: 'test-key',
      model: 'claude-sonnet-4-20250514',
    });

    const chunks: string[] = [];
    let finalUsage: { inputTokens: number; outputTokens: number } | undefined;

    for await (const event of client.chatStream({
      messages: [{ role: 'user', content: 'Hello' }],
    })) {
      if (event.type === 'content' && event.content) {
        chunks.push(event.content);
      }
      if (event.type === 'done' && event.usage) {
        finalUsage = event.usage;
      }
    }

    expect(chunks.length).toBeGreaterThan(0);
    expect(chunks.join('')).toBe('Hello from Claude!');
    expect(finalUsage).toEqual({ inputTokens: 10, outputTokens: 5 });
  });
});

describe('AnthropicClient tool use', () => {
  it('passes tools to API and parses tool_use response', async () => {
    mockCreate.mockResolvedValueOnce({
      content: [
        { type: 'tool_use', id: 'toolu_01', name: 'shell.exec', input: { command: 'ls' } },
      ],
      stop_reason: 'tool_use',
      usage: { input_tokens: 20, output_tokens: 15 },
    });

    const client = new AnthropicClient({
      apiKey: 'test-key',
      model: 'claude-sonnet-4-20250514',
    });

    const response = await client.chat({
      messages: [{ role: 'user', content: 'list files' }],
      tools: [{
        name: 'shell.exec',
        description: 'Run shell command',
        input_schema: { type: 'object', properties: { command: { type: 'string' } }, required: ['command'] },
      }],
    });

    expect(response.stopReason).toBe('tool_use');
    expect(response.toolCalls).toHaveLength(1);
    expect(response.toolCalls![0]).toEqual({
      id: 'toolu_01',
      name: 'shell.exec',
      args: { command: 'ls' },
    });
  });
});
