import { createServer } from 'http';
import express from 'express';
import quoteRoutes from './quote';
import { getCanonicalTokenBySymbol } from '../../config/canonical-tokens';

var mockGetPoolsByToken: jest.Mock;
var mockGetLiveDodoPools: jest.Mock;

jest.mock('../../database/repositories/pool-repo', () => {
  mockGetPoolsByToken = jest.fn();
  return {
    __esModule: true,
    PoolRepository: jest.fn().mockImplementation(() => ({
      getPoolsByToken: mockGetPoolsByToken,
    })),
  };
});
jest.mock('../../services/live-dodo-fallback', () => {
  mockGetLiveDodoPools = jest.fn();
  return {
    __esModule: true,
    getLiveDodoPools: mockGetLiveDodoPools,
  };
});
/** Real GRU loader filters pools by on-file routable addresses; synthetic test pools would be dropped. */
jest.mock('../../config/gru-transport', () => ({
  filterPoolsForRouting: <T>(_chainId: number, pools: T[]) => pools,
}));
jest.mock('../middleware/cache');

function createApp() {
  const app = express();
  app.use('/api/v1', quoteRoutes);
  return app;
}

async function startServer(app: express.Application): Promise<{ server: ReturnType<typeof createServer>; baseUrl: string }> {
  const server = createServer(app);
  await new Promise<void>((resolve) => server.listen(0, () => resolve()));
  const port = (server.address() as { port: number }).port;
  return { server, baseUrl: `http://127.0.0.1:${port}` };
}

describe('Quote API', () => {
  let server: ReturnType<typeof createServer>;
  let baseUrl: string;

  beforeAll(async () => {
    const started = await startServer(createApp());
    server = started.server;
    baseUrl = started.baseUrl;
  });

  beforeEach(() => {
    mockGetPoolsByToken.mockReset();
    mockGetLiveDodoPools.mockReset();
  });

  afterAll((done) => {
    server.close(done);
  });

  it('quotes staged cUSDT_V2 against active cUSDT liquidity on Chain 138', async () => {
    const cusdtV2 = getCanonicalTokenBySymbol(138, 'cUSDT_V2');
    const cusdtV1 = getCanonicalTokenBySymbol(138, 'cUSDT');
    if (!cusdtV2?.addresses[138] || !cusdtV1?.addresses[138]) {
      throw new Error('cUSDT_V2 / cUSDT Chain 138 addresses required for this test');
    }
    const tokenInV2 = String(cusdtV2.addresses[138]);
    const cusdtV1Lookup = String(cusdtV1.addresses[138]).toLowerCase();

    mockGetPoolsByToken.mockImplementation(async (_chainId: number, tokenAddress: string) => {
      if (tokenAddress.toLowerCase() === cusdtV1Lookup) {
        return [
          {
            chainId: 138,
            poolAddress: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
            token0Address: cusdtV1Lookup,
            token1Address: '0x004b63a7b5b0e06f6bb6adb4a5f9f590bf3182d1',
            dexType: 'dodo',
            reserve0: '1000000',
            reserve1: '1000000',
            reserve0Usd: 1000000,
            reserve1Usd: 1000000,
            totalLiquidityUsd: 2000000,
            volume24h: 10000,
            lastUpdated: new Date(),
          },
        ];
      }
      return [];
    });

    const res = await fetch(
      `${baseUrl}/api/v1/quote?chainId=138&tokenIn=${encodeURIComponent(tokenInV2)}&tokenOut=0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1&amountIn=1000`
    );
    expect(res.status).toBe(200);
    const body = (await res.json()) as Record<string, any>;
    expect(body.amountOut).toBeTruthy();
    expect(['constant-product', 'pmm-onchain']).toContain(body.quoteEngine);
    expect(body.poolAddress).toBe('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
    expect(body.canonicalLiquidity).toMatchObject({
      requestedTokenInSymbol: 'cUSDT_V2',
      lookupTokenInSymbol: 'cUSDT',
      lookupTokenInAddress: cusdtV1Lookup,
      usedFallback: true,
    });
    expect(mockGetPoolsByToken).toHaveBeenCalledWith(138, cusdtV1Lookup);
  });

  it('falls back to live DODO pools when indexed liquidity is empty', async () => {
    mockGetPoolsByToken.mockResolvedValue([]);
    mockGetLiveDodoPools.mockResolvedValue([
      {
        chainId: 138,
        poolAddress: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
        token0Address: '0x93e66202a11b1772e55407b32b44e5cd8eda7f22',
        token1Address: '0xf22258f57794cc8e06237084b353ab30fffa640b',
        dexType: 'dodo',
        factoryAddress: '0x86ada6ef91a3b450f89f2b751e93b1b7a3218895',
        reserve0: '1000000',
        reserve1: '1000000',
        reserve0Usd: 1000000,
        reserve1Usd: 1000000,
        totalLiquidityUsd: 2000000,
        volume24h: 0,
        lastUpdated: new Date(),
      },
    ]);

    const res = await fetch(
      `${baseUrl}/api/v1/quote?chainId=138&tokenIn=0x93E66202A11B1772E55407B32B44e5Cd8eda7f22&tokenOut=0xf22258f57794CC8E06237084b353Ab30fFfa640b&amountIn=1000`
    );
    expect(res.status).toBe(200);
    const body = (await res.json()) as Record<string, any>;
    expect(body.amountOut).toBeTruthy();
    expect(['constant-product', 'pmm-onchain']).toContain(body.quoteEngine);
    expect(body.poolAddress).toBe('0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb');
    expect(body.executorAddress).toBe('0x86ada6ef91a3b450f89f2b751e93b1b7a3218895');
    expect(mockGetLiveDodoPools).toHaveBeenCalledWith(138);
  });
});
