import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { authService } from '../authService';
import { api } from '../api';

// Mock the api module
vi.mock('../api', () => ({
  api: {
    post: vi.fn(),
    get: vi.fn(),
  },
}));

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

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('login', () => {
    it('should login successfully and return user and token', async () => {
      const mockResponse = {
        user: {
          id: '123',
          username: 'testuser',
          email: 'test@example.com',
          first_name: 'Test',
          last_name: 'User',
          avatar: 'https://example.com/avatar.jpg',
          created_at: '2024-01-01T00:00:00Z',
          is_verified: false,
        },
        access_token: 'access-token-123',
        refresh_token: 'refresh-token-123',
      };

      vi.mocked(api.post).mockResolvedValue(mockResponse);

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

      expect(api.post).toHaveBeenCalledWith('/auth/login', {
        email: 'test@example.com',
        password: 'password123',
      });
      expect(result.user).toBeDefined();
      expect(result.user.id).toBe('123');
      expect(result.user.email).toBe('test@example.com');
      expect(result.token).toBeDefined();
      expect(result.token.access_token).toBe('access-token-123');
    });

    it('should handle login errors', async () => {
      const mockError = new Error('Invalid credentials');
      vi.mocked(api.post).mockRejectedValue(mockError);

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

  describe('register', () => {
    it('should register successfully and return user and token', async () => {
      const mockResponse = {
        user: {
          id: '456',
          username: 'newuser',
          email: 'new@example.com',
          first_name: 'New',
          last_name: 'User',
          avatar: null,
          created_at: '2024-01-01T00:00:00Z',
          is_verified: false,
        },
        access_token: 'access-token-456',
        refresh_token: 'refresh-token-456',
      };

      vi.mocked(api.post).mockResolvedValue(mockResponse);

      const result = await authService.register({
        email: 'new@example.com',
        password: 'password123',
        username: 'newuser',
      });

      expect(api.post).toHaveBeenCalledWith('/auth/register', {
        email: 'new@example.com',
        password: 'password123',
        username: 'newuser',
      });
      expect(result.user).toBeDefined();
      expect(result.user.id).toBe('456');
      expect(result.user.username).toBe('newuser');
      expect(result.token.access_token).toBe('access-token-456');
    });
  });

  describe('logout', () => {
    it('should logout successfully', async () => {
      vi.mocked(api.post).mockResolvedValue({});

      await authService.logout();

      expect(api.post).toHaveBeenCalledWith('/auth/logout');
    });

    it('should handle logout errors gracefully', async () => {
      const mockError = new Error('Network error');
      vi.mocked(api.post).mockRejectedValue(mockError);

      // Should not throw, just log warning
      const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
      await authService.logout();
      expect(consoleSpy).toHaveBeenCalled();
      consoleSpy.mockRestore();
    });
  });

  describe('getCurrentUser', () => {
    it('should get current user successfully', async () => {
      const mockResponse = {
        user: {
          id: '123',
          username: 'testuser',
          email: 'test@example.com',
          first_name: 'Test',
          last_name: 'User',
          avatar: 'https://example.com/avatar.jpg',
          created_at: '2024-01-01T00:00:00Z',
          is_verified: true,
        },
      };

      vi.mocked(api.get).mockResolvedValue(mockResponse);

      const result = await authService.getCurrentUser();

      expect(api.get).toHaveBeenCalledWith('/auth/me');
      expect(result).toBeDefined();
      expect(result.id).toBe('123');
      expect(result.email).toBe('test@example.com');
      expect(result.tier).toBe('Pro'); // is_verified = true
    });
  });

  describe('checkUsername', () => {
    it('should check username availability', async () => {
      const mockResponse = { available: true };
      vi.mocked(api.get).mockResolvedValue(mockResponse);

      const result = await authService.checkUsername('testuser');

      expect(api.get).toHaveBeenCalledWith(
        '/auth/check-username?username=testuser',
      );
      expect(result).toEqual(mockResponse);
    });

    it('should return false for unavailable username', async () => {
      const mockResponse = { available: false };
      vi.mocked(api.get).mockResolvedValue(mockResponse);

      const result = await authService.checkUsername('takenuser');
      expect(result.available).toBe(false);
    });
  });

  describe('verifyEmail', () => {
    it('should verify email with token', async () => {
      vi.mocked(api.post).mockResolvedValue({});

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

      expect(api.post).toHaveBeenCalledWith('/auth/verify-email', {
        token: 'verification-token-123',
      });
    });
  });

  describe('resendVerification', () => {
    it('should resend verification email', async () => {
      vi.mocked(api.post).mockResolvedValue({});

      await authService.resendVerification('test@example.com');

      expect(api.post).toHaveBeenCalledWith('/auth/resend-verification', {
        email: 'test@example.com',
      });
    });
  });
});
