import { describe, expect, it } from 'vitest';
import { createServiceApp } from '../index.js';

describe('createServiceApp', () => {
  it('returns a Fastify instance', async () => {
    const app = await createServiceApp({
      name: 'test-service',
      version: '0.0.1',
      logger: false,
    });
    expect(app).toBeDefined();
    expect(typeof app.listen).toBe('function');
    expect(typeof app.get).toBe('function');
    await app.close();
  });

  it('has a /health endpoint returning correct shape', async () => {
    const app = await createServiceApp({
      name: 'my-service',
      version: '1.2.3',
      description: 'A test service',
      logger: false,
    });

    const res = await app.inject({ method: 'GET', url: '/health' });
    expect(res.statusCode).toBe(200);

    const body = JSON.parse(res.payload);
    expect(body.status).toBe('ok');
    expect(body.service).toBe('my-service');
    expect(body.version).toBe('1.2.3');
    expect(body.description).toBe('A test service');
    expect(body.timestamp).toBeTruthy();
    expect(body.requestId).toBeTruthy();

    await app.close();
  });

  it('propagates x-request-id header', async () => {
    const app = await createServiceApp({
      name: 'test',
      version: '0.1.0',
      logger: false,
    });

    const customId = 'req-12345';
    const res = await app.inject({
      method: 'GET',
      url: '/health',
      headers: { 'x-request-id': customId },
    });

    expect(res.headers['x-request-id']).toBe(customId);
    const body = JSON.parse(res.payload);
    expect(body.requestId).toBe(customId);

    await app.close();
  });

  it('generates x-request-id when not provided', async () => {
    const app = await createServiceApp({
      name: 'test',
      version: '0.1.0',
      logger: false,
    });

    const res = await app.inject({ method: 'GET', url: '/health' });
    expect(res.headers['x-request-id']).toBeTruthy();
    // Should be UUID format
    expect(res.headers['x-request-id']).toMatch(
      /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
    );

    await app.close();
  });

  it('handles ServiceError with correct status code', async () => {
    const { NotFoundError } = await import('@bytelyst/errors');
    const app = await createServiceApp({
      name: 'test',
      version: '0.1.0',
      logger: false,
    });

    app.get('/fail', async () => {
      throw new NotFoundError('User not found');
    });

    const res = await app.inject({ method: 'GET', url: '/fail' });
    expect(res.statusCode).toBe(404);
    const body = JSON.parse(res.payload);
    expect(body.error).toBe('User not found');

    await app.close();
  });

  it('handles ServiceError with details', async () => {
    const { BadRequestError } = await import('@bytelyst/errors');
    const app = await createServiceApp({
      name: 'test',
      version: '0.1.0',
      logger: false,
    });

    app.get('/bad', async () => {
      throw new BadRequestError('Validation failed', { field: 'email' });
    });

    const res = await app.inject({ method: 'GET', url: '/bad' });
    expect(res.statusCode).toBe(400);
    const body = JSON.parse(res.payload);
    expect(body.error).toBe('Validation failed');
    expect(body.details).toEqual({ field: 'email' });

    await app.close();
  });

  it('handles unknown errors as 500', async () => {
    const app = await createServiceApp({
      name: 'test',
      version: '0.1.0',
      logger: false,
    });

    app.get('/crash', async () => {
      throw new Error('unexpected');
    });

    const res = await app.inject({ method: 'GET', url: '/crash' });
    expect(res.statusCode).toBe(500);
    const body = JSON.parse(res.payload);
    expect(body.error).toBe('Internal server error');

    await app.close();
  });

  it('omits description from health when not provided', async () => {
    const app = await createServiceApp({
      name: 'minimal',
      version: '0.1.0',
      logger: false,
    });

    const res = await app.inject({ method: 'GET', url: '/health' });
    const body = JSON.parse(res.payload);
    expect(body.description).toBeUndefined();

    await app.close();
  });
});
