/**
 * Tests for Service Error Handler Utility
 * FE-TEST-004: Test service error handler utility functions
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AxiosError } from 'axios';
import {
  handleServiceError,
  withErrorHandling,
  getServiceValidationErrors,
  isErrorStatus,
  isNetworkError,
  getUserFriendlyMessage,
  handleApiServiceError,
} from './serviceErrorHandler';
import type { ApiError } from '@/schemas/apiSchemas';

// Mock dependencies
vi.mock('./apiErrorHandler', () => ({
  parseApiError: vi.fn((error: unknown): ApiError => {
    if (error && typeof error === 'object' && 'code' in error) {
      return error as ApiError;
    }
    return {
      code: 0,
      message: 'Unknown error',
      timestamp: new Date().toISOString(),
    };
  }),
  formatErrorMessage: vi.fn((error: ApiError) => error.message),
  getValidationErrors: vi.fn((error: ApiError) => {
    if (error.details) {
      const errors: Record<string, string> = {};
      error.details.forEach((detail: any) => {
        if (detail.field && detail.message) {
          errors[detail.field] = detail.message;
        }
      });
      return errors;
    }
    return {};
  }),
}));

vi.mock('./errorMessages', () => ({
  formatUserFriendlyError: vi.fn((error: ApiError) => error.message),
  isRetryableError: vi.fn(() => false),
}));

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

  describe('handleServiceError', () => {
    it('should throw error by default', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      expect(() => handleServiceError(error)).toThrow();
    });

    it('should return message when throwError is false', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const result = handleServiceError(error, { throwError: false });
      expect(result).toBe('Not found');
    });

    it('should use custom message override', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      expect(() => {
        handleServiceError(error, {
          customMessages: { 404: 'Custom not found' },
        });
      }).toThrow('Custom not found');
    });

    it('should include context in error', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      try {
        handleServiceError(error, { context: 'playlist' });
      } catch (e: any) {
        expect(e.apiError).toBeDefined();
      }
    });
  });

  describe('withErrorHandling', () => {
    it('should return result on success', async () => {
      const apiCall = vi.fn().mockResolvedValue('success');
      const result = await withErrorHandling(apiCall);
      expect(result).toBe('success');
    });

    it('should handle errors', async () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const apiCall = vi.fn().mockRejectedValue(error);
      await expect(withErrorHandling(apiCall)).rejects.toThrow();
    });
  });

  describe('getServiceValidationErrors', () => {
    it('should extract validation errors', () => {
      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 = getServiceValidationErrors(error);
      expect(result.email).toBe('Invalid email');
      expect(result.password).toBe('Too short');
    });
  });

  describe('isErrorStatus', () => {
    it('should return true for matching status', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      expect(isErrorStatus(error, 404)).toBe(true);
    });

    it('should return false for non-matching status', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      expect(isErrorStatus(error, 500)).toBe(false);
    });
  });

  describe('isNetworkError', () => {
    it('should return true for AxiosError without response', () => {
      const error = {
        isAxiosError: true,
        request: {},
        response: undefined,
      } as unknown as AxiosError;
      expect(isNetworkError(error)).toBe(true);
    });

    it('should return false for AxiosError with response', () => {
      const error = {
        isAxiosError: true,
        request: {},
        response: { status: 404 },
      } as unknown as AxiosError;
      expect(isNetworkError(error)).toBe(false);
    });
  });

  describe('getUserFriendlyMessage', () => {
    it('should return user-friendly message', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const result = getUserFriendlyMessage(error);
      expect(result).toBe('Not found');
    });

    it('should use context', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      const result = getUserFriendlyMessage(error, 'playlist');
      expect(result).toBe('Not found');
    });
  });

  describe('handleApiServiceError', () => {
    it('should always throw', () => {
      const error: ApiError = {
        code: 404,
        message: 'Not found',
        timestamp: new Date().toISOString(),
      };
      expect(() => handleApiServiceError(error)).toThrow();
    });
  });
});
