import { describe, it, expect, afterEach } from 'vitest';
import Fastify from 'fastify';
import type { FastifyInstance } from 'fastify';
import { ZodError, z } from 'zod';
import { errorHandler } from '../src/middleware/error-handler.js';

let app: FastifyInstance;

afterEach(async () => {
  if (app) await app.close();
});

function setupApp() {
  app = Fastify({ logger: false });
  app.setErrorHandler(errorHandler);
  return app;
}

describe('errorHandler', () => {
  it('returns 400 for ZodError', async () => {
    const a = setupApp();
    a.get('/test', async () => {
      z.object({ name: z.string() }).parse({});
    });
    await a.ready();

    const res = await a.inject({ method: 'GET', url: '/test' });
    expect(res.statusCode).toBe(400);
    const body = res.json<{ error: string; details: unknown[] }>();
    expect(body.error).toBe('Validation error');
    expect(body.details).toBeDefined();
  });

  it('returns 500 for unknown errors and hides details', async () => {
    const a = setupApp();
    a.get('/test', async () => {
      throw new Error('secret database password leaked');
    });
    await a.ready();

    const res = await a.inject({ method: 'GET', url: '/test' });
    expect(res.statusCode).toBe(500);
    const body = res.json<{ error: string }>();
    expect(body.error).toBe('Internal server error');
    expect(JSON.stringify(body)).not.toContain('secret');
  });

  it('returns correct status for HTTP errors', async () => {
    const a = setupApp();
    a.get('/test', async (_req, reply) => {
      reply.code(404).send({ error: 'Not found', statusCode: 404 });
    });
    await a.ready();

    const res = await a.inject({ method: 'GET', url: '/test' });
    expect(res.statusCode).toBe(404);
  });

  it('returns 429 for rate limit errors', async () => {
    const a = setupApp();
    a.get('/test', async () => {
      const err = new Error('Rate limit') as Error & { statusCode: number };
      err.statusCode = 429;
      throw err;
    });
    await a.ready();

    const res = await a.inject({ method: 'GET', url: '/test' });
    expect(res.statusCode).toBe(429);
    expect(res.json<{ error: string }>().error).toBe('Rate limit exceeded');
  });
});
