import { describe, it, expect, vi, Mocked } from 'vitest';
import { AuthService } from './AuthService';
import { AuthApiClient } from '../../api/auth/AuthApiClient';
import { SessionViewModel } from '../../view-models/SessionViewModel';

describe('AuthService', () => {
  let mockApiClient: Mocked<AuthApiClient>;
  let service: AuthService;

  beforeEach(() => {
    mockApiClient = {
      signup: vi.fn(),
      login: vi.fn(),
      logout: vi.fn(),
      getIracingAuthUrl: vi.fn(),
    } as Mocked<AuthApiClient>;

    service = new AuthService(mockApiClient);
  });

  describe('signup', () => {
    it('should call apiClient.signup and return SessionViewModel', async () => {
      const params = {
        email: 'test@example.com',
        password: 'password123',
        displayName: 'Test User',
      };

      const mockResponse = {
        token: 'jwt-token',
        user: {
          userId: 'user-123',
          email: 'test@example.com',
          displayName: 'Test User',
        },
      };

      mockApiClient.signup.mockResolvedValue(mockResponse);

      const result = await service.signup(params);

      expect(mockApiClient.signup).toHaveBeenCalledWith(params);
      expect(result).toBeInstanceOf(SessionViewModel);
      expect(result.userId).toBe('user-123');
      expect(result.email).toBe('test@example.com');
      expect(result.displayName).toBe('Test User');
      expect(result.isAuthenticated).toBe(true);
    });

    it('should throw error when apiClient.signup fails', async () => {
      const params = {
        email: 'test@example.com',
        password: 'password123',
        displayName: 'Test User',
      };

      const error = new Error('Signup failed');
      mockApiClient.signup.mockRejectedValue(error);

      await expect(service.signup(params)).rejects.toThrow('Signup failed');
    });
  });

  describe('login', () => {
    it('should call apiClient.login and return SessionViewModel', async () => {
      const params = {
        email: 'test@example.com',
        password: 'password123',
      };

      const mockResponse = {
        token: 'jwt-token',
        user: {
          userId: 'user-123',
          email: 'test@example.com',
          displayName: 'Test User',
        },
      };

      mockApiClient.login.mockResolvedValue(mockResponse);

      const result = await service.login(params);

      expect(mockApiClient.login).toHaveBeenCalledWith(params);
      expect(result).toBeInstanceOf(SessionViewModel);
      expect(result.userId).toBe('user-123');
      expect(result.email).toBe('test@example.com');
      expect(result.displayName).toBe('Test User');
      expect(result.isAuthenticated).toBe(true);
    });

    it('should throw error when apiClient.login fails', async () => {
      const params = {
        email: 'test@example.com',
        password: 'password123',
      };

      const error = new Error('Login failed');
      mockApiClient.login.mockRejectedValue(error);

      await expect(service.login(params)).rejects.toThrow('Login failed');
    });
  });

  describe('logout', () => {
    it('should call apiClient.logout', async () => {
      mockApiClient.logout.mockResolvedValue(undefined);

      await service.logout();

      expect(mockApiClient.logout).toHaveBeenCalled();
    });

    it('should throw error when apiClient.logout fails', async () => {
      const error = new Error('Logout failed');
      mockApiClient.logout.mockRejectedValue(error);

      await expect(service.logout()).rejects.toThrow('Logout failed');
    });
  });

  describe('getIracingAuthUrl', () => {
    it('should call apiClient.getIracingAuthUrl with returnTo', () => {
      const returnTo = '/dashboard';
      const expectedUrl = 'https://api.example.com/auth/iracing/start?returnTo=%2Fdashboard';

      mockApiClient.getIracingAuthUrl.mockReturnValue(expectedUrl);

      const result = service.getIracingAuthUrl(returnTo);

      expect(mockApiClient.getIracingAuthUrl).toHaveBeenCalledWith(returnTo);
      expect(result).toBe(expectedUrl);
    });

    it('should call apiClient.getIracingAuthUrl without returnTo', () => {
      const expectedUrl = 'https://api.example.com/auth/iracing/start';

      mockApiClient.getIracingAuthUrl.mockReturnValue(expectedUrl);

      const result = service.getIracingAuthUrl();

      expect(mockApiClient.getIracingAuthUrl).toHaveBeenCalledWith(undefined);
      expect(result).toBe(expectedUrl);
    });
  });
});