/**
 * ReadImage vision call records to the per-user usage ledger (issue #499).
 * The vision call is a raw fetch outside OpenAICompatClient, so it records
 * directly via recordLlmUsage.
 */
import { describe, it, expect, vi, afterEach } from 'vitest';
import { callVisionModel } from './image.js';
import type { ToolContext } from './core.js';
import { setLlmUsageRecorder, type LlmUsageEvent } from '../../llm/usage-recorder.js';

function ctx(): ToolContext {
  return {
    workspacePath: '/tmp',
    userId: 'u1',
    toolsConfig: { visionBaseUrl: 'http://vision-host:11434/v1', visionModel: 'qwen2-vl' },
  } as unknown as ToolContext;
}

afterEach(() => {
  setLlmUsageRecorder(null);
  vi.unstubAllGlobals();
});

describe('callVisionModel usage recording', () => {
  it('records a direct usage event with the vision host as route', async () => {
    const events: LlmUsageEvent[] = [];
    setLlmUsageRecorder((e) => events.push(e));
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        model: 'qwen2-vl',
        choices: [{ message: { content: 'a cat' } }],
        usage: { prompt_tokens: 250, completion_tokens: 12 },
      }),
    }));

    const res = await callVisionModel('data:image/png;base64,xxx', 'describe', ctx());
    expect(res.isError).toBe(false);
    expect(events).toHaveLength(1);
    expect(events[0]).toEqual({
      userId: 'u1', source: 'direct', model: 'qwen2-vl', route: 'vision-host:11434',
      tokensIn: 250, tokensOut: 12,
    });
  });

  it('records zero tokens when the vision response omits usage', async () => {
    const events: LlmUsageEvent[] = [];
    setLlmUsageRecorder((e) => events.push(e));
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({ choices: [{ message: { content: 'x' } }] }),
    }));
    await callVisionModel('data:image/png;base64,xxx', '', ctx());
    expect(events[0]).toMatchObject({ tokensIn: 0, tokensOut: 0, model: 'qwen2-vl' });
  });

  it('does not record on a vision API error', async () => {
    const events: LlmUsageEvent[] = [];
    setLlmUsageRecorder((e) => events.push(e));
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false, status: 500, text: async () => 'boom' }));
    const res = await callVisionModel('data:image/png;base64,xxx', '', ctx());
    expect(res.isError).toBe(true);
    expect(events).toHaveLength(0);
  });
});
