import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AxiosError } from 'axios';
import { getSettings, updateSettings } from './settingsService';
import { apiClient } from '@/services/api/client';
import { UserSettings, UpdateSettingsRequest } from '../types/settings';

// Mock apiClient
vi.mock('@/services/api/client', () => ({
  apiClient: {
    get: vi.fn(),
    put: vi.fn(),
  },
}));

describe('settingsService', () => {
  const mockSettings: UserSettings = {
    notifications: {
      email_notifications: true,
      push_notifications: true,
      browser_notifications: true,
      email_on_follow: true,
      email_on_like: true,
      email_on_comment: true,
      email_on_message: true,
      email_on_mention: true,
      email_marketing: false,
    },
    privacy: {
      allow_search_indexing: true,
      show_activity: true,
    },
    content: {
      explicit_content: false,
      autoplay: true,
    },
    preferences: {
      language: 'en',
      timezone: 'UTC',
      theme: 'auto',
    },
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('getSettings', () => {
    it('should return settings successfully', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({
        data: mockSettings,
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {} as any,
      });

      const result = await getSettings(1);

      expect(result).toEqual(mockSettings);
      expect(apiClient.get).toHaveBeenCalledWith('/users/1/settings');
    });

    it('should throw error on 401 Unauthorized', async () => {
      const error = new AxiosError('Unauthorized');
      error.response = {
        status: 401,
        data: {},
        statusText: 'Unauthorized',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSettings(1)).rejects.toThrow(
        'Unauthorized: Please log in to access settings',
      );
    });

    it('should throw error on 403 Forbidden', async () => {
      const error = new AxiosError('Forbidden');
      error.response = {
        status: 403,
        data: {},
        statusText: 'Forbidden',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSettings(1)).rejects.toThrow(
        'Forbidden: You cannot access these settings',
      );
    });

    it('should throw error on 404 Not Found', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: {},
        statusText: 'Not Found',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSettings(1)).rejects.toThrow('Settings not found');
    });

    it('should throw error with custom message from response', async () => {
      const error = new AxiosError('Error');
      error.response = {
        status: 500,
        data: { error: 'Internal server error' },
        statusText: 'Internal Server Error',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSettings(1)).rejects.toThrow('Internal server error');
    });

    it('should throw generic error on unknown error', async () => {
      const error = new Error('Network error');
      vi.mocked(apiClient.get).mockRejectedValue(error);

      await expect(getSettings(1)).rejects.toThrow(error);
    });
  });

  describe('updateSettings', () => {
    const updateRequest: UpdateSettingsRequest = {
      preferences: {
        language: 'fr',
        timezone: 'Europe/Paris',
        theme: 'dark',
      },
    };

    it('should update settings successfully', async () => {
      vi.mocked(apiClient.put).mockResolvedValue({
        data: {},
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {} as any,
      });

      await updateSettings(1, updateRequest);

      expect(apiClient.put).toHaveBeenCalledWith(
        '/users/1/settings',
        updateRequest,
      );
    });

    it('should throw error on 400 Bad Request', async () => {
      const error = new AxiosError('Bad Request');
      error.response = {
        status: 400,
        data: { error: 'Invalid settings data' },
        statusText: 'Bad Request',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(
        'Invalid settings data',
      );
    });

    it('should throw error on 401 Unauthorized', async () => {
      const error = new AxiosError('Unauthorized');
      error.response = {
        status: 401,
        data: {},
        statusText: 'Unauthorized',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(
        'Unauthorized: Please log in to update settings',
      );
    });

    it('should throw error on 403 Forbidden', async () => {
      const error = new AxiosError('Forbidden');
      error.response = {
        status: 403,
        data: {},
        statusText: 'Forbidden',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(
        'Forbidden: You cannot update these settings',
      );
    });

    it('should throw error on 404 Not Found', async () => {
      const error = new AxiosError('Not Found');
      error.response = {
        status: 404,
        data: {},
        statusText: 'Not Found',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(
        'Settings not found',
      );
    });

    it('should throw error with custom message from response', async () => {
      const error = new AxiosError('Error');
      error.response = {
        status: 500,
        data: { error: 'Internal server error' },
        statusText: 'Internal Server Error',
        headers: {},
        config: {} as any,
      };

      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(
        'Internal server error',
      );
    });

    it('should throw generic error on unknown error', async () => {
      const error = new Error('Network error');
      vi.mocked(apiClient.put).mockRejectedValue(error);

      await expect(updateSettings(1, updateRequest)).rejects.toThrow(error);
    });

    it('should update only notifications', async () => {
      const notificationsOnly: UpdateSettingsRequest = {
        notifications: {
          email_notifications: false,
        },
      };

      vi.mocked(apiClient.put).mockResolvedValue({
        data: {},
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {} as any,
      });

      await updateSettings(1, notificationsOnly);

      expect(apiClient.put).toHaveBeenCalledWith(
        '/users/1/settings',
        notificationsOnly,
      );
    });

    it('should update only privacy', async () => {
      const privacyOnly: UpdateSettingsRequest = {
        privacy: {
          allow_search_indexing: false,
        },
      };

      vi.mocked(apiClient.put).mockResolvedValue({
        data: {},
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {} as any,
      });

      await updateSettings(1, privacyOnly);

      expect(apiClient.put).toHaveBeenCalledWith(
        '/users/1/settings',
        privacyOnly,
      );
    });

    it('should update only content', async () => {
      const contentOnly: UpdateSettingsRequest = {
        content: {
          explicit_content: true,
        },
      };

      vi.mocked(apiClient.put).mockResolvedValue({
        data: {},
        status: 200,
        statusText: 'OK',
        headers: {},
        config: {} as any,
      });

      await updateSettings(1, contentOnly);

      expect(apiClient.put).toHaveBeenCalledWith(
        '/users/1/settings',
        contentOnly,
      );
    });
  });
});
