/**
 * Tests for Error Messages Utility
 * FE-TEST-004: Test error message utility functions
 */

import { describe, it, expect } from 'vitest';
import {
  ERROR_MESSAGES,
  CONTEXT_ERROR_MESSAGES,
  getErrorMessageByStatus,
  getContextErrorMessage,
  formatUserFriendlyError,
  isRetryableError,
  getRetryDelay,
} from './errorMessages';
import type { ApiError } from '@/schemas/apiSchemas';

describe('errorMessages utilities', () => {
  describe('ERROR_MESSAGES', () => {
    it('should have messages for common status codes', () => {
      expect(ERROR_MESSAGES[400]).toBeTruthy();
      expect(ERROR_MESSAGES[401]).toBeTruthy();
      expect(ERROR_MESSAGES[404]).toBeTruthy();
      expect(ERROR_MESSAGES[500]).toBeTruthy();
    });

    it('should have network error messages', () => {
      expect(ERROR_MESSAGES.NETWORK).toBeTruthy();
      expect(ERROR_MESSAGES.TIMEOUT).toBeTruthy();
      expect(ERROR_MESSAGES.UNKNOWN).toBeTruthy();
    });
  });

  describe('getErrorMessageByStatus', () => {
    it('should return message for known status', () => {
      expect(getErrorMessageByStatus(404)).toBe(ERROR_MESSAGES[404]);
      expect(getErrorMessageByStatus(500)).toBe(ERROR_MESSAGES[500]);
    });

    it('should return default message for unknown status', () => {
      expect(getErrorMessageByStatus(999)).toBe(ERROR_MESSAGES.UNKNOWN);
    });

    it('should use custom default message', () => {
      const custom = 'Custom error';
      expect(getErrorMessageByStatus(999, custom)).toBe(custom);
    });
  });

  describe('getContextErrorMessage', () => {
    it('should return context-specific message', () => {
      expect(getContextErrorMessage('auth', 'login')).toBe(
        CONTEXT_ERROR_MESSAGES.auth.login,
      );
    });

    it('should return default for unknown context/action', () => {
      expect(getContextErrorMessage('auth', 'unknown')).toBe(
        ERROR_MESSAGES.UNKNOWN,
      );
    });

    it('should use custom default message', () => {
      const custom = 'Custom error';
      expect(getContextErrorMessage('auth', 'unknown', custom)).toBe(custom);
    });
  });

  describe('formatUserFriendlyError', () => {
    it('should format ApiError', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const result = formatUserFriendlyError(error);
      expect(result).toBe(ERROR_MESSAGES[404]);
    });

    it('should format Error instance', () => {
      const error = new Error('Test error');
      const result = formatUserFriendlyError(error);
      expect(result).toBe('Test error');
    });

    it('should format with context', () => {
      const error: ApiError = {
        code: 404,
        message: 'not found',
        timestamp: new Date().toISOString(),
      };
      const result = formatUserFriendlyError(error, 'playlist');
      expect(result).toBe(CONTEXT_ERROR_MESSAGES.playlist.notFound);
    });

    it('should include details when requested', () => {
      const error: ApiError = {
        code: 422,
        message: 'Validation failed',
        timestamp: new Date().toISOString(),
        details: [
          { field: 'email', message: 'Invalid email' },
          { field: 'password', message: 'Too short' },
        ],
      };
      const result = formatUserFriendlyError(error, undefined, true);
      // Details are included in parentheses
      expect(result).toContain('Invalid email');
      expect(result).toContain('Too short');
    });

    it('should handle network errors', () => {
      const error = { code: 'ERR_NETWORK' };
      const result = formatUserFriendlyError(error);
      expect(result).toBe(ERROR_MESSAGES.NETWORK);
    });

    it('should handle timeout errors', () => {
      const error = { code: 'ECONNABORTED' };
      const result = formatUserFriendlyError(error);
      expect(result).toBe(ERROR_MESSAGES.TIMEOUT);
    });

    it('should return unknown for unhandled errors', () => {
      const result = formatUserFriendlyError(null);
      expect(result).toBe(ERROR_MESSAGES.UNKNOWN);
    });
  });

  describe('isRetryableError', () => {
    it('should return true for retryable status codes', () => {
      const error429: ApiError = {
        code: 429,
        message: 'Rate limit',
        timestamp: new Date().toISOString(),
      };
      const error500: ApiError = {
        code: 500,
        message: 'Server error',
        timestamp: new Date().toISOString(),
      };
      const error503: ApiError = {
        code: 503,
        message: 'Service unavailable',
        timestamp: new Date().toISOString(),
      };

      expect(isRetryableError(error429)).toBe(true);
      expect(isRetryableError(error500)).toBe(true);
      expect(isRetryableError(error503)).toBe(true);
    });

    it('should return true for network errors', () => {
      expect(isRetryableError({ code: 'ERR_NETWORK' })).toBe(true);
      expect(isRetryableError({ code: 'ECONNABORTED' })).toBe(true);
    });

    it('should return false for non-retryable errors', () => {
      const error404: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const error401: ApiError = {
        code: 401,
        message: 'Unauthorized',
        timestamp: new Date().toISOString(),
      };

      expect(isRetryableError(error404)).toBe(false);
      expect(isRetryableError(error401)).toBe(false);
    });
  });

  describe('getRetryDelay', () => {
    it('should use retry_after from rate limit error', () => {
      const error: ApiError = {
        code: 429,
        message: 'Rate limit',
        timestamp: new Date().toISOString(),
        retry_after: 5,
      };
      expect(getRetryDelay(error, 0)).toBe(5000);
    });

    it('should use exponential backoff', () => {
      const error: ApiError = {
        code: 500,
        message: 'Server error',
        timestamp: new Date().toISOString(),
      };
      expect(getRetryDelay(error, 0)).toBe(1000);
      expect(getRetryDelay(error, 1)).toBe(2000);
      expect(getRetryDelay(error, 2)).toBe(4000);
      expect(getRetryDelay(error, 3)).toBe(8000);
    });

    it('should cap at 30 seconds', () => {
      const error: ApiError = {
        code: 500,
        message: 'Server error',
        timestamp: new Date().toISOString(),
      };
      expect(getRetryDelay(error, 10)).toBe(30000);
    });
  });
});
