import { describe, it, expect, vi } from 'vitest';
import { Router } from './router.js';
import type { GatewayRequest, OutboundMessage } from './protocol.js';
import { makeResponse, ErrorCode } from './protocol.js';

describe('Router', () => {
  it('dispatches to registered handler', async () => {
    const router = new Router();
    const handler = vi.fn(async (req: GatewayRequest) => makeResponse(req.id, { ok: true }));
    router.register('test.method', handler);

    const request: GatewayRequest = { id: 1, method: 'test.method', params: {} };
    const send = vi.fn();
    const result = await router.dispatch(request, send);

    expect(handler).toHaveBeenCalledWith(request, send);
    expect(result).toEqual({ id: 1, result: { ok: true } });
  });

  it('returns MethodNotFound for unregistered method', async () => {
    const router = new Router();
    const request: GatewayRequest = { id: 1, method: 'unknown.method' };
    const send = vi.fn();
    const result = await router.dispatch(request, send);

    expect(result).toEqual({
      id: 1,
      error: { code: ErrorCode.MethodNotFound, message: 'Unknown method: unknown.method' },
    });
  });

  it('lists registered methods', () => {
    const router = new Router();
    router.register('a.method', async () => {});
    router.register('b.method', async () => {});

    expect(router.listMethods()).toEqual(['a.method', 'b.method']);
  });

  it('handler can send streaming events via send function', async () => {
    const router = new Router();
    router.register('stream.test', async (req, send) => {
      send({ id: req.id, event: 'content', data: { text: 'chunk1' } });
      send({ id: req.id, event: 'content', data: { text: 'chunk2' } });
      send({ id: req.id, event: 'done', data: { content: 'chunk1chunk2' } });
    });

    const request: GatewayRequest = { id: 1, method: 'stream.test' };
    const sent: OutboundMessage[] = [];
    const send = vi.fn((msg: OutboundMessage) => sent.push(msg));

    await router.dispatch(request, send);

    expect(sent).toHaveLength(3);
    expect(sent[0]).toEqual({ id: 1, event: 'content', data: { text: 'chunk1' } });
    expect(sent[2]).toEqual({ id: 1, event: 'done', data: { content: 'chunk1chunk2' } });
  });
});
