import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals';
import Fastify from 'fastify';
import { signAgentToken, type AgentTokenData } from '../security/agent-token.js';

const TEST_SECRET = 'test-signing-secret-at-least-32-chars!!';
const ORIGINAL_ENV = { ...process.env };

jest.mock('@4runr/shared', () => ({
  env: {
    NODE_ENV: 'development',
    RATE_LIMIT_ENABLED: false,
    AUTH_ENABLED: false,
    AUTH_MODE: 'api_key',
  },
  createLogger: () => ({
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
  }),
}));

jest.mock('../middleware/rateLimit.js', () => ({
  strictRateLimit: async () => {},
  writeRateLimit: async () => {},
  readRateLimit: async () => {},
}));

jest.mock('../middleware/auth.js', () => ({
  requireAuth: async () => {},
}));

function mintToken(tools: string[]): string {
  const payload: AgentTokenData = {
    agent_id: 'proxy-test-agent',
    tools,
    permissions: ['read'],
    expires_at: new Date(Date.now() + 3600_000).toISOString(),
  };
  const signed = signAgentToken(payload, TEST_SECRET);
  if (!('token' in signed)) throw new Error(signed.error);
  return signed.token;
}

describe('POST /api/tool-proxy', () => {
  let app: ReturnType<typeof Fastify>;

  beforeEach(async () => {
    process.env = {
      ...ORIGINAL_ENV,
      NODE_ENV: 'development',
      SIGNING_SECRET: TEST_SECRET,
    };
    app = Fastify();
    const { toolProxyRoutes } = await import('../routes/tool-proxy.js');
    await toolProxyRoutes(app);
  });

  afterEach(async () => {
    await app.close();
    process.env = ORIGINAL_ENV;
  });

  it('returns 403 when tool is not in token allowlist', async () => {
    const agent_token = mintToken(['http_fetch']);

    const res = await app.inject({
      method: 'POST',
      url: '/api/tool-proxy',
      payload: {
        agent_token,
        tool: 'serpapi',
        action: 'search',
        params: { q: 'test' },
      },
    });

    expect(res.statusCode).toBe(403);
    const body = res.json() as { error: string; allowedTools?: string[] };
    expect(body.error).toBe('Tool not permitted');
    expect(body.allowedTools).toEqual(['http_fetch']);
  });

  it('returns 403 for invalid agent_token before allowlist', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/tool-proxy',
      payload: {
        agent_token: 'bad.token',
        tool: 'http_fetch',
        action: 'get',
        params: { url: 'https://example.com' },
      },
    });

    expect(res.statusCode).toBe(403);
    const body = res.json() as { error: string };
    expect(body.error).toBe('Invalid token');
  });

  it('returns 400 when required body fields are missing', async () => {
    const res = await app.inject({
      method: 'POST',
      url: '/api/tool-proxy',
      payload: { agent_token: mintToken(['http_fetch']) },
    });

    expect(res.statusCode).toBe(400);
    const body = res.json() as { error: string };
    expect(body.error).toBe('Missing required fields');
  });
});
