/**
 * Tests for error handler
 */

import { describe, it, expect, vi } from 'vitest';
import { AppError, errorHandler, createRetryableError, createNonRetryableError } from './error-handler';
import type { FastifyRequest, FastifyReply } from 'fastify';

describe('Error Handler', () => {
  describe('AppError', () => {
    it('should create an error with message and status code', () => {
      const error = new AppError(400, 'TEST_ERROR', 'Test error');
      expect(error.message).toBe('Test error');
      expect(error.statusCode).toBe(400);
      expect(error.code).toBe('TEST_ERROR');
      expect(error.retryable).toBe(false);
    });

    it('should create a retryable error', () => {
      const error = new AppError(500, 'RETRYABLE_ERROR', 'Retryable error', undefined, true);
      expect(error.retryable).toBe(true);
    });

    it('should include context in error', () => {
      const error = new AppError(500, 'ERROR', 'Error with context', { userId: '123', action: 'test' });
      const context = error.getContext();
      expect(context.details).toEqual({ userId: '123', action: 'test' });
    });
  });

  describe('createRetryableError', () => {
    it('should create a retryable error', () => {
      const error = createRetryableError(503, 'RETRYABLE_ERROR', 'Retryable error');
      expect(error.retryable).toBe(true);
      expect(error.statusCode).toBe(503);
    });
  });

  describe('createNonRetryableError', () => {
    it('should create a non-retryable error', () => {
      const error = createNonRetryableError(400, 'NON_RETRYABLE_ERROR', 'Non-retryable error');
      expect(error.retryable).toBe(false);
      expect(error.statusCode).toBe(400);
    });
  });

  describe('errorHandler', () => {
    it('should handle AppError correctly', async () => {
      const request = {
        id: 'test-request-id',
        log: {
          error: vi.fn(),
          warn: vi.fn(),
          info: vi.fn(),
        },
      } as unknown as FastifyRequest;

      const reply = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis(),
        code: vi.fn().mockReturnThis(),
      } as unknown as FastifyReply;

      const error = new AppError(400, 'TEST_ERROR', 'Test error');
      
      await errorHandler(error, request, reply);

      expect(reply.status).toHaveBeenCalledWith(400);
      expect(reply.send).toHaveBeenCalledWith({
        error: {
          code: 'TEST_ERROR',
          message: 'Test error',
          details: undefined,
        },
      });
    });

    it('should handle generic errors', async () => {
      const request = {
        id: 'test-request-id',
        log: {
          error: vi.fn(),
          warn: vi.fn(),
          info: vi.fn(),
        },
      } as unknown as FastifyRequest;

      const reply = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis(),
        code: vi.fn().mockReturnThis(),
      } as unknown as FastifyReply;

      const error = new Error('Generic error');
      
      await errorHandler(error, request, reply);

      expect(reply.status).toHaveBeenCalledWith(500);
      expect(reply.send).toHaveBeenCalledWith({
        error: expect.objectContaining({
          code: 'INTERNAL_ERROR',
        }),
      });
    });

    it('should include retryable flag in response', async () => {
      const request = {
        id: 'test-request-id',
        log: {
          error: vi.fn(),
          warn: vi.fn(),
          info: vi.fn(),
        },
      } as unknown as FastifyRequest;

      const reply = {
        status: vi.fn().mockReturnThis(),
        send: vi.fn().mockReturnThis(),
        code: vi.fn().mockReturnThis(),
      } as unknown as FastifyReply;

      const error = createRetryableError(503, 'RETRYABLE_ERROR', 'Retryable error');
      
      await errorHandler(error, request, reply);

      expect(reply.status).toHaveBeenCalledWith(503);
      expect(reply.send).toHaveBeenCalledWith({
        error: {
          code: 'RETRYABLE_ERROR',
          message: 'Retryable error',
          details: undefined,
        },
      });
    });
  });
});

