import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { GatewayEvent, GatewayRequest, OutboundMessage } from '../protocol.js';
import { LaneQueue } from '../lane-queue.js';
import { createAgentHandlers } from './agent.js';
import { CommandRegistry, registerBuiltinCommands } from '../../commands/index.js';

describe('createAgentHandlers command fast-path', () => {
  const mockAgent = {
    process: vi.fn(async () => 'agent response'),
    getUsage: vi.fn(() => ({
      primary: { inputTokens: 10, outputTokens: 5, calls: 1 },
      delegation: {},
      total: { inputTokens: 10, outputTokens: 5, calls: 1, estimatedCost: 0 },
    })),
    getModelTier: vi.fn(() => 'default'),
    setModelTier: vi.fn(),
    compact: vi.fn(async () => null),
    reset: vi.fn(),
  };

  const sessionBridge = {
    getAgent: vi.fn(() => mockAgent),
    getSessionId: vi.fn(() => 'ws:conn-1'),
    setBusy: vi.fn(),
    setOnToolUse: vi.fn(),
    isBusy: vi.fn(() => false),
  };

  const sessionManager = {
    setSessionConfig: vi.fn(),
    deleteSessionConfig: vi.fn(),
  };

  const commandRegistry = new CommandRegistry();
  registerBuiltinCommands(commandRegistry);

  const handlers = createAgentHandlers({
    sessionBridge: sessionBridge as any,
    laneQueue: new LaneQueue(),
    sessionManager: sessionManager as any,
    commandRegistry,
  });

  beforeEach(() => {
    vi.clearAllMocks();
    mockAgent.process.mockResolvedValue('agent response');
    mockAgent.compact.mockResolvedValue(null);
  });

  it('handles known commands without calling agent.process', async () => {
    const sent: OutboundMessage[] = [];
    const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
    const req: GatewayRequest = {
      id: 1,
      method: 'agent.send',
      params: { message: '/usage', connectionId: 'conn-1' },
    };

    await handlers['agent.send'](req, send);

    expect(mockAgent.process).not.toHaveBeenCalled();
    expect(sent).toHaveLength(1);
    const event = sent[0] as GatewayEvent;
    expect(event.event).toBe('done');
    expect((event.data as { content: string }).content).toContain('Token Usage');
  });

  it('handles metadata commands via fast-path', async () => {
    const sent: OutboundMessage[] = [];
    const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
    const req: GatewayRequest = {
      id: 2,
      method: 'agent.send',
      params: {
        message: '/reset',
        connectionId: 'conn-1',
        metadata: { isCommand: true, command: 'reset' },
      },
    };

    await handlers['agent.send'](req, send);

    expect(mockAgent.reset).toHaveBeenCalledOnce();
    expect(sessionManager.deleteSessionConfig).toHaveBeenCalledWith('ws', 'ws:conn-1', 'modelTier');
    expect(mockAgent.process).not.toHaveBeenCalled();
    expect(((sent[0] as GatewayEvent).data as { content: string }).content).toContain('Session reset.');
  });

  it('handles /model command via fast-path and persists session tier', async () => {
    const sent: OutboundMessage[] = [];
    const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
    const req: GatewayRequest = {
      id: 4,
      method: 'agent.send',
      params: {
        message: '/model fast',
        connectionId: 'conn-1',
        metadata: { isCommand: true, command: 'model', commandArgs: 'fast' },
      },
    };

    await handlers['agent.send'](req, send);

    expect(mockAgent.setModelTier).toHaveBeenCalledWith('fast');
    expect(sessionManager.setSessionConfig).toHaveBeenCalledWith('ws', 'ws:conn-1', 'modelTier', 'fast');
    expect(mockAgent.process).not.toHaveBeenCalled();
    expect(((sent[0] as GatewayEvent).data as { content: string }).content).toContain('Switched to model tier: fast');
  });

  it('falls through to agent.process for unknown commands', async () => {
    const sent: OutboundMessage[] = [];
    const send = vi.fn((msg: OutboundMessage) => sent.push(msg));
    const req: GatewayRequest = {
      id: 3,
      method: 'agent.send',
      params: { message: '/not-a-real-command', connectionId: 'conn-1' },
    };

    await handlers['agent.send'](req, send);

    expect(mockAgent.process).toHaveBeenCalledWith('/not-a-real-command', undefined);
    expect((sent[0] as GatewayEvent).event).toBe('done');
    expect(((sent[0] as GatewayEvent).data as { content: string }).content).toBe('agent response');
  });
});
