import { describe, expect, it, vi } from 'vitest';

import { OrderRouter } from '../packages/execution/src/orderRouter';
import type { Order, OrderBook } from '../packages/domain/src/types';

const sampleBook: OrderBook = {
  bids: [{ px: 100, sz: 1 }],
  asks: [{ px: 100.5, sz: 1 }],
  ts: Date.now()
};

describe('OrderRouter', () => {
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

  it('sends limit order within slip guard', async () => {
    const send = vi.fn(async (order: Order) => ({ id: order.clientId }));
    const router = new OrderRouter(send, () => sampleBook, { maxBps: 100 });

    const order: Order = {
      clientId: 'o-1',
      symbol: 'BTC',
      side: 'buy',
      px: 100.55,
      sz: 0.1,
      tif: 'GTC'
    };

    const id = await router.sendLimit(order);
    expect(id).toMatch(uuidRegex);
    expect(send).toHaveBeenCalledWith(expect.objectContaining({ clientId: id }));
    expect(send).toHaveBeenCalledTimes(1);
  });

  it('rejects orders exceeding slip guard', async () => {
    const send = vi.fn(async (order: Order) => ({ id: order.clientId }));
    const router = new OrderRouter(send, () => sampleBook, { maxBps: 10 });

    const order: Order = {
      clientId: 'o-2',
      symbol: 'BTC',
      side: 'buy',
      px: 102,
      sz: 0.1,
      tif: 'GTC'
    };

    await expect(router.sendLimit(order)).rejects.toThrow(/slippage/);
    expect(send).not.toHaveBeenCalled();
  });

  it('rejects post-only orders that would cross', async () => {
    const send = vi.fn(async (order: Order) => ({ id: order.clientId }));
    const router = new OrderRouter(send, () => sampleBook, { maxBps: 100 });

    const order: Order = {
      clientId: 'o-3',
      symbol: 'BTC',
      side: 'buy',
      px: 100.5,
      sz: 0.1,
      tif: 'GTC',
      postOnly: true
    };

    await expect(router.sendLimit(order)).rejects.toThrow(/post-only/);
    expect(send).not.toHaveBeenCalled();
  });

  it('throttles orders based on min interval and enforces unique client ids', async () => {
    const send = vi.fn(async (order: Order) => ({ id: order.clientId }));
    const router = new OrderRouter(send, () => sampleBook, { maxBps: 100, minIntervalMs: 10 });

    const orderA: Order = {
      clientId: 'o-4',
      symbol: 'BTC',
      side: 'buy',
      px: 100.4,
      sz: 0.1,
      tif: 'GTC'
    };

    const orderB: Order = { ...orderA, clientId: 'o-5', px: 100.45 };

    await router.sendLimit(orderA);
    expect(send).toHaveBeenCalledTimes(1);

    const start = Date.now();
    await router.sendLimit(orderB);
    const elapsed = Date.now() - start;
    expect(send).toHaveBeenCalledTimes(2);
    expect(elapsed).toBeGreaterThanOrEqual(8);

    await expect(router.sendLimit(orderB)).rejects.toThrow(/duplicate clientId/);
  });
});
