/**
 * Tests for Timeout Handler Utility
 * FE-TEST-004: Test timeout handler utility functions
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import {
  TIMEOUT_CONFIG,
  TIMEOUT_MESSAGES,
  createTimeoutPromise,
  withTimeout,
  getTimeoutForRequestType,
  isTimeoutError,
  getTimeoutMessage,
  withRequestTimeout,
} from './timeoutHandler';

// Mock react-hot-toast
vi.mock('react-hot-toast', () => ({
  default: {
    loading: vi.fn(() => 'toast-id'),
    dismiss: vi.fn(),
  },
}));

describe('timeoutHandler utilities', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe('TIMEOUT_CONFIG', () => {
    it('should have timeout configs', () => {
      expect(TIMEOUT_CONFIG.default).toBe(10000);
      expect(TIMEOUT_CONFIG.fast).toBe(5000);
      expect(TIMEOUT_CONFIG.slow).toBe(30000);
    });
  });

  describe('createTimeoutPromise', () => {
    it('should reject after timeout', async () => {
      const promise = createTimeoutPromise(1000, 'Timeout');
      vi.advanceTimersByTime(1000);
      await expect(promise).rejects.toThrow('Timeout');
    });
  });

  describe('withTimeout', () => {
    it('should resolve if promise completes before timeout', async () => {
      const promise = Promise.resolve('success');
      const result = withTimeout(promise, { timeout: 1000 });
      vi.advanceTimersByTime(500);
      await expect(result).resolves.toBe('success');
    });

    it('should reject on timeout', async () => {
      const promise = new Promise((resolve) => {
        setTimeout(() => resolve('success'), 2000);
      });
      const result = withTimeout(promise, { timeout: 1000 });
      vi.advanceTimersByTime(1000);
      await expect(result).rejects.toThrow();
    });

    it('should call onTimeout callback', async () => {
      const onTimeout = vi.fn();
      const promise = new Promise((resolve) => {
        setTimeout(() => resolve('success'), 2000);
      });
      const result = withTimeout(promise, { timeout: 1000, onTimeout });
      vi.advanceTimersByTime(1000);
      try {
        await result;
      } catch {
        // Expected
      }
      expect(onTimeout).toHaveBeenCalled();
    });
  });

  describe('getTimeoutForRequestType', () => {
    it('should return timeout for request type', () => {
      expect(getTimeoutForRequestType('fast')).toBe(5000);
      expect(getTimeoutForRequestType('normal')).toBe(10000);
      expect(getTimeoutForRequestType('slow')).toBe(30000);
    });

    it('should default to normal', () => {
      expect(getTimeoutForRequestType()).toBe(10000);
    });
  });

  describe('isTimeoutError', () => {
    it('should return true for timeout error', () => {
      const error = new Error('Request timeout');
      expect(isTimeoutError(error)).toBe(true);
    });

    it('should return true for timeout in message', () => {
      const error = new Error('Request expired');
      expect(isTimeoutError(error)).toBe(true);
    });

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

    it('should return false for other errors', () => {
      const error = new Error('Other error');
      expect(isTimeoutError(error)).toBe(false);
    });
  });

  describe('getTimeoutMessage', () => {
    it('should return message for request type', () => {
      expect(getTimeoutMessage('fast')).toBeTruthy();
      expect(getTimeoutMessage('normal')).toBeTruthy();
      expect(getTimeoutMessage('slow')).toBeTruthy();
    });
  });

  describe('withRequestTimeout', () => {
    it('should wrap API call with timeout', async () => {
      const apiCall = vi.fn().mockResolvedValue('success');
      const result = withRequestTimeout(apiCall, 'normal');
      vi.advanceTimersByTime(500);
      await expect(result).resolves.toBe('success');
    });
  });
});
