import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock dependencies before any imports
const mockPost = vi.fn();
const mockClearTokens = vi.fn();

vi.mock('../tokenStorage', () => {
  return {
    TokenStorage: {
      clearTokens: () => mockClearTokens(),
      getAccessToken: vi.fn(),
      getRefreshToken: vi.fn(),
      setTokens: vi.fn(),
      hasTokens: vi.fn(),
    },
  };
});

vi.mock('./client', async () => {
  const actual = await vi.importActual<typeof import('./client')>('./client');
  return {
    ...actual,
    apiClient: {
      post: (...args: any[]) => mockPost(...args),
      get: vi.fn(),
      put: vi.fn(),
      delete: vi.fn(),
      patch: vi.fn(),
      interceptors: {
        request: { use: vi.fn(), eject: vi.fn() },
        response: { use: vi.fn(), eject: vi.fn() },
      },
    },
  };
});

// Now import the logout function
import { logout } from './auth';

describe('auth API service - logout', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockPost.mockReset();
    mockClearTokens.mockReset();
  });

  describe('logout', () => {
    it('should call logout API endpoint and clear tokens', async () => {
      // Mock successful API call
      mockPost.mockResolvedValue({ data: {} });
      mockClearTokens.mockReturnValue(undefined);

      await logout();

      // Verify API call was made
      expect(mockPost).toHaveBeenCalledWith('/auth/logout');

      // Verify tokens were cleared
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });

    it('should clear tokens even if API call fails', async () => {
      // Mock failed API call
      const apiError = new Error('API Error');
      mockPost.mockRejectedValue(apiError);

      // Should not throw, but clear tokens anyway
      await expect(logout()).resolves.toBeUndefined();

      // Verify tokens were still cleared despite error
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });

    it('should clear tokens even if API call throws network error', async () => {
      // Mock network error
      const networkError = new Error('Network Error');
      mockPost.mockRejectedValue(networkError);

      await expect(logout()).resolves.toBeUndefined();

      // Verify tokens were still cleared
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });

    it('should clear tokens even if API call returns 401', async () => {
      // Mock 401 error
      const axiosError = {
        response: {
          status: 401,
          data: { error: 'Unauthorized' },
        },
        isAxiosError: true,
      };
      mockPost.mockRejectedValue(axiosError);

      await expect(logout()).resolves.toBeUndefined();

      // Verify tokens were still cleared
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });

    it('should clear tokens even if API call times out', async () => {
      // Mock timeout error
      const timeoutError = new Error('Request timeout');
      mockPost.mockRejectedValue(timeoutError);

      await expect(logout()).resolves.toBeUndefined();

      // Verify tokens were still cleared
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });

    it('should always clear tokens in finally block', async () => {
      // Mock successful call
      mockPost.mockResolvedValue({ data: {} });

      await logout();

      // Verify clearTokens was called exactly once
      expect(mockClearTokens).toHaveBeenCalledTimes(1);

      // Reset and test with error
      vi.clearAllMocks();
      mockPost.mockRejectedValue(new Error('Error'));

      await logout();

      // Verify clearTokens was still called
      expect(mockClearTokens).toHaveBeenCalledTimes(1);
    });
  });
});
