/**
 * Tests for the Auth Service
 * @jest-environment jsdom
 */

import { authService } from '@/services/auth.service';
import { api } from '@/services/api';
import { API_ENDPOINTS } from '@/utils/constants';

// Mock the api module
jest.mock('@/services/api', () => ({
  api: {
    get: jest.fn(),
    post: jest.fn(),
    put: jest.fn(),
    patch: jest.fn(),
    delete: jest.fn(),
  },
  ApiError: class ApiError extends Error {
    constructor(public status: number, message: string, public data?: unknown) {
      super(message);
      this.name = 'ApiError';
    }
  },
}));

describe('Auth Service', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('login', () => {
    it('should login with valid credentials', async () => {
      const mockResponse = {
        success: true,
        token: 'jwt-token-123',
        user: {
          id: '1',
          email: 'test@test.com',
          firstName: 'John',
          lastName: 'Doe',
          role: 'CLIENT',
        },
      };

      (api.post as jest.Mock).mockResolvedValueOnce(mockResponse);

      const result = await authService.login({
        email: 'test@test.com',
        password: 'password123',
      });

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.LOGIN,
        { email: 'test@test.com', password: 'password123' }
      );
      expect(result).toEqual({
        token: 'jwt-token-123',
        user: mockResponse.user,
      });
    });

    it('should throw error on invalid credentials', async () => {
      (api.post as jest.Mock).mockRejectedValueOnce(new Error('Invalid credentials'));

      await expect(
        authService.login({ email: 'test@test.com', password: 'wrong' })
      ).rejects.toThrow('Invalid credentials');
    });
  });

  describe('register', () => {
    it('should register a new user', async () => {
      const mockResponse = {
        success: true,
        token: 'jwt-token-new',
        user: {
          id: '2',
          email: 'new@test.com',
          firstName: 'Jane',
          lastName: 'Doe',
          role: 'CLIENT',
        },
      };

      (api.post as jest.Mock).mockResolvedValueOnce(mockResponse);

      const registerData = {
        email: 'new@test.com',
        password: 'password123',
        firstName: 'Jane',
        lastName: 'Doe',
      };

      const result = await authService.register(registerData);

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.REGISTER,
        registerData
      );
      expect(result.token).toBe('jwt-token-new');
      expect(result.user.email).toBe('new@test.com');
    });

    it('should throw error if email already exists', async () => {
      (api.post as jest.Mock).mockRejectedValueOnce(new Error('Email already exists'));

      await expect(
        authService.register({
          email: 'existing@test.com',
          password: 'password',
          firstName: 'Test',
          lastName: 'User',
        })
      ).rejects.toThrow('Email already exists');
    });
  });

  describe('logout', () => {
    it('should call logout endpoint', async () => {
      (api.post as jest.Mock).mockResolvedValueOnce({ success: true });

      await authService.logout();

      expect(api.post).toHaveBeenCalledWith(API_ENDPOINTS.AUTH.LOGOUT);
    });
  });

  describe('getCurrentUser', () => {
    it('should return user data from response.data', async () => {
      const mockUser = {
        id: '1',
        email: 'test@test.com',
        firstName: 'John',
        lastName: 'Doe',
        role: 'CLIENT',
      };

      (api.get as jest.Mock).mockResolvedValueOnce({
        success: true,
        data: mockUser,
      });

      const result = await authService.getCurrentUser();

      expect(api.get).toHaveBeenCalledWith(API_ENDPOINTS.AUTH.ME);
      expect(result).toEqual(mockUser);
    });

    it('should return user data from response.user', async () => {
      const mockUser = {
        id: '1',
        email: 'test@test.com',
        firstName: 'John',
        lastName: 'Doe',
        role: 'CLIENT',
      };

      (api.get as jest.Mock).mockResolvedValueOnce({
        success: true,
        user: mockUser,
      });

      const result = await authService.getCurrentUser();

      expect(result).toEqual(mockUser);
    });

    it('should return response directly if no data or user field', async () => {
      const mockUser = {
        id: '1',
        email: 'test@test.com',
        firstName: 'John',
        lastName: 'Doe',
        role: 'CLIENT',
      };

      (api.get as jest.Mock).mockResolvedValueOnce(mockUser);

      const result = await authService.getCurrentUser();

      expect(result).toEqual(mockUser);
    });
  });

  describe('googleLogin', () => {
    it('should login with Google token', async () => {
      const mockResponse = {
        success: true,
        token: 'jwt-google-token',
        user: {
          id: '3',
          email: 'google@test.com',
          firstName: 'Google',
          lastName: 'User',
          role: 'CLIENT',
        },
      };

      (api.post as jest.Mock).mockResolvedValueOnce(mockResponse);

      const result = await authService.googleLogin('google-oauth-token');

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.GOOGLE,
        { token: 'google-oauth-token' }
      );
      expect(result.token).toBe('jwt-google-token');
    });
  });

  describe('facebookLogin', () => {
    it('should login with Facebook token', async () => {
      const mockResponse = {
        success: true,
        token: 'jwt-facebook-token',
        user: {
          id: '4',
          email: 'facebook@test.com',
          firstName: 'Facebook',
          lastName: 'User',
          role: 'CLIENT',
        },
      };

      (api.post as jest.Mock).mockResolvedValueOnce(mockResponse);

      const result = await authService.facebookLogin('facebook-oauth-token');

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.FACEBOOK,
        { token: 'facebook-oauth-token' }
      );
      expect(result.token).toBe('jwt-facebook-token');
    });
  });

  describe('verifyEmail', () => {
    it('should verify email with token', async () => {
      (api.post as jest.Mock).mockResolvedValueOnce({ success: true });

      await authService.verifyEmail('verification-token');

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.VERIFY_EMAIL,
        { token: 'verification-token' }
      );
    });
  });

  describe('forgotPassword', () => {
    it('should send forgot password request', async () => {
      (api.post as jest.Mock).mockResolvedValueOnce({ success: true });

      await authService.forgotPassword('test@test.com');

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.FORGOT_PASSWORD,
        { email: 'test@test.com' }
      );
    });
  });

  describe('resetPassword', () => {
    it('should reset password with token', async () => {
      (api.post as jest.Mock).mockResolvedValueOnce({ success: true });

      await authService.resetPassword('reset-token', 'newPassword123');

      expect(api.post).toHaveBeenCalledWith(
        API_ENDPOINTS.AUTH.RESET_PASSWORD,
        { token: 'reset-token', password: 'newPassword123' }
      );
    });
  });
});
