const {
  AppError,
  ValidationError,
  AuthenticationError,
  ForbiddenError,
  NotFoundError,
  ConflictError,
  RateLimitError,
  DockerError,
  CaddyError,
  DNSError,
  ServiceUnavailableError
} = require('../src/utilities/errors');

describe('Error Classes', () => {
  describe('AppError', () => {
    it('has default statusCode 500 and auto-generated code', () => {
      const err = new AppError('something broke');
      expect(err.message).toBe('something broke');
      expect(err.statusCode).toBe(500);
      expect(err.code).toBe('APP_ERROR');
      expect(err.isOperational).toBe(true);
      expect(err).toBeInstanceOf(Error);
    });

    it('accepts custom statusCode and code', () => {
      const err = new AppError('custom', 418, 'DC-TEAPOT');
      expect(err.statusCode).toBe(418);
      expect(err.code).toBe('DC-TEAPOT');
    });
  });

  describe('ValidationError', () => {
    it('has statusCode 400, code DC-400, and optional field', () => {
      const err = new ValidationError('bad input', 'email');
      expect(err.statusCode).toBe(400);
      expect(err.code).toBe('DC-400');
      expect(err.field).toBe('email');
      expect(err).toBeInstanceOf(AppError);
    });

    it('field defaults to null', () => {
      const err = new ValidationError('bad');
      expect(err.field).toBeNull();
    });
  });

  describe('AuthenticationError', () => {
    it('has statusCode 401 and requiresTotp flag', () => {
      const err = new AuthenticationError('need auth', true);
      expect(err.statusCode).toBe(401);
      expect(err.code).toBe('DC-401');
      expect(err.requiresTotp).toBe(true);
      expect(err).toBeInstanceOf(AppError);
    });

    it('has sensible defaults', () => {
      const err = new AuthenticationError();
      expect(err.message).toBe('Authentication required');
      expect(err.requiresTotp).toBe(false);
    });
  });

  describe('ForbiddenError', () => {
    it('has statusCode 403', () => {
      const err = new ForbiddenError();
      expect(err.statusCode).toBe(403);
      expect(err.code).toBe('DC-403');
      expect(err.message).toBe('Forbidden');
      expect(err).toBeInstanceOf(AppError);
    });
  });

  describe('NotFoundError', () => {
    it('has statusCode 404 and resource in message', () => {
      const err = new NotFoundError('Service');
      expect(err.statusCode).toBe(404);
      expect(err.code).toBe('DC-404');
      expect(err.message).toBe('Service not found');
      expect(err.resource).toBe('Service');
      expect(err).toBeInstanceOf(AppError);
    });

    it('defaults to "Resource"', () => {
      const err = new NotFoundError();
      expect(err.message).toBe('Resource not found');
    });
  });

  describe('ConflictError', () => {
    it('has statusCode 409 and optional conflictingResource', () => {
      const err = new ConflictError('already exists', 'service-x');
      expect(err.statusCode).toBe(409);
      expect(err.code).toBe('DC-409');
      expect(err.conflictingResource).toBe('service-x');
      expect(err).toBeInstanceOf(AppError);
    });
  });

  describe('RateLimitError', () => {
    it('has statusCode 429 and retryAfter', () => {
      const err = new RateLimitError(30);
      expect(err.statusCode).toBe(429);
      expect(err.code).toBe('DC-429');
      expect(err.retryAfter).toBe(30);
      expect(err.message).toBe('Rate limit exceeded');
      expect(err).toBeInstanceOf(AppError);
    });

    it('defaults retryAfter to 60', () => {
      const err = new RateLimitError();
      expect(err.retryAfter).toBe(60);
    });
  });

  describe('DockerError', () => {
    it('has statusCode 500, operation, and details', () => {
      const err = new DockerError('container failed', 'create', { containerId: '123' });
      expect(err.statusCode).toBe(500);
      expect(err.code).toBe('DC-500-DOCKER');
      expect(err.operation).toBe('create');
      expect(err.details).toEqual({ containerId: '123' });
      expect(err).toBeInstanceOf(AppError);
    });
  });

  describe('CaddyError', () => {
    it('has statusCode 502', () => {
      const err = new CaddyError('reload failed', 'reload');
      expect(err.statusCode).toBe(502);
      expect(err.code).toBe('DC-502-CADDY');
      expect(err.operation).toBe('reload');
      expect(err).toBeInstanceOf(AppError);
    });
  });

  describe('DNSError', () => {
    it('has statusCode 502', () => {
      const err = new DNSError('zone create failed', 'create-zone');
      expect(err.statusCode).toBe(502);
      expect(err.code).toBe('DC-502-DNS');
      expect(err).toBeInstanceOf(AppError);
    });
  });

  describe('ServiceUnavailableError', () => {
    it('has statusCode 503, service name, and optional retryAfter', () => {
      const err = new ServiceUnavailableError('plex', 120);
      expect(err.statusCode).toBe(503);
      expect(err.code).toBe('DC-503');
      expect(err.message).toBe('Service unavailable: plex');
      expect(err.service).toBe('plex');
      expect(err.retryAfter).toBe(120);
      expect(err).toBeInstanceOf(AppError);
    });
  });
});
