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

// Mock apiClient
vi.mock('../client', () => ({
  default: {
    get: vi.fn(),
    post: vi.fn(),
    patch: vi.fn(),
    delete: vi.fn(),
  },
}));

import {
  getProfile,
  updateProfile,
  uploadAvatar,
  deleteAvatar,
  sendVerificationEmail,
  verifyEmail,
  requestEmailChange,
  confirmEmailChange,
  changePassword,
  setupTOTP,
  verifyTOTP,
  disableTOTP,
  getRecoveryCodes,
  regenerateRecoveryCodes,
  getSessions,
  revokeSession,
  revokeOtherSessions,
  getLoginHistory,
  sendPhoneVerification,
  verifyPhoneCode,
  getUserEmails,
  addUserEmail,
  deleteUserEmail,
  sendUserEmailVerification,
  verifyUserEmail,
  setPrimaryEmail,
} from '../profile';
import apiClient from '../client';

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

  describe('getProfile', () => {
    it('fetches user profile', async () => {
      const mockProfile = {
        id: 1,
        email: 'test@example.com',
        name: 'Test User',
        email_verified: true,
      };
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockProfile });

      const result = await getProfile();

      expect(apiClient.get).toHaveBeenCalledWith('/auth/profile/');
      expect(result).toEqual(mockProfile);
    });
  });

  describe('updateProfile', () => {
    it('updates profile with provided data', async () => {
      const mockUpdated = { id: 1, name: 'Updated Name' };
      vi.mocked(apiClient.patch).mockResolvedValue({ data: mockUpdated });

      const result = await updateProfile({ name: 'Updated Name' });

      expect(apiClient.patch).toHaveBeenCalledWith('/auth/profile/', { name: 'Updated Name' });
      expect(result).toEqual(mockUpdated);
    });
  });

  describe('uploadAvatar', () => {
    it('uploads avatar file', async () => {
      const mockResponse = { avatar_url: 'https://example.com/avatar.jpg' };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const file = new File(['test'], 'avatar.jpg', { type: 'image/jpeg' });
      const result = await uploadAvatar(file);

      expect(apiClient.post).toHaveBeenCalledWith(
        '/auth/profile/avatar/',
        expect.any(FormData),
        { headers: { 'Content-Type': 'multipart/form-data' } }
      );
      expect(result.avatar_url).toBe('https://example.com/avatar.jpg');
    });
  });

  describe('deleteAvatar', () => {
    it('deletes user avatar', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

      await deleteAvatar();

      expect(apiClient.delete).toHaveBeenCalledWith('/auth/profile/avatar/');
    });
  });

  describe('email verification', () => {
    it('sends verification email', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await sendVerificationEmail();

      expect(apiClient.post).toHaveBeenCalledWith('/auth/email/verify/send/');
    });

    it('verifies email with token', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await verifyEmail('verification-token');

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

  describe('email change', () => {
    it('requests email change', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await requestEmailChange('new@example.com');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/email/change/', {
        new_email: 'new@example.com',
      });
    });

    it('confirms email change', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await confirmEmailChange('change-token');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/email/change/confirm/', {
        token: 'change-token',
      });
    });
  });

  describe('changePassword', () => {
    it('changes password with current and new', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await changePassword('oldPassword', 'newPassword');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/password/change/', {
        current_password: 'oldPassword',
        new_password: 'newPassword',
      });
    });
  });

  describe('2FA / TOTP', () => {
    it('sets up TOTP', async () => {
      const mockSetup = {
        secret: 'ABCD1234',
        qr_code: 'base64...',
        provisioning_uri: 'otpauth://...',
      };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockSetup });

      const result = await setupTOTP();

      expect(apiClient.post).toHaveBeenCalledWith('/auth/mfa/totp/setup/');
      expect(result.secret).toBe('ABCD1234');
    });

    it('verifies TOTP code', async () => {
      const mockResponse = { success: true, backup_codes: ['code1', 'code2'] };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });

      const result = await verifyTOTP('123456');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/mfa/totp/verify/', { code: '123456' });
      expect(result.success).toBe(true);
      expect(result.recovery_codes).toEqual(['code1', 'code2']);
    });

    it('disables TOTP', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await disableTOTP('123456');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/mfa/disable/', { mfa_code: '123456' });
    });

    it('gets recovery codes status', async () => {
      vi.mocked(apiClient.get).mockResolvedValue({ data: {} });

      const result = await getRecoveryCodes();

      expect(apiClient.get).toHaveBeenCalledWith('/auth/mfa/backup-codes/status/');
      expect(result).toEqual([]);
    });

    it('regenerates recovery codes', async () => {
      const mockCodes = ['code1', 'code2', 'code3'];
      vi.mocked(apiClient.post).mockResolvedValue({ data: { backup_codes: mockCodes } });

      const result = await regenerateRecoveryCodes();

      expect(apiClient.post).toHaveBeenCalledWith('/auth/mfa/backup-codes/');
      expect(result).toEqual(mockCodes);
    });
  });

  describe('sessions', () => {
    it('gets sessions', async () => {
      const mockSessions = [
        { id: '1', device_info: 'Chrome', ip_address: '1.1.1.1', is_current: true },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockSessions });

      const result = await getSessions();

      expect(apiClient.get).toHaveBeenCalledWith('/auth/sessions/');
      expect(result).toEqual(mockSessions);
    });

    it('revokes session', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

      await revokeSession('session-123');

      expect(apiClient.delete).toHaveBeenCalledWith('/auth/sessions/session-123/');
    });

    it('revokes other sessions', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await revokeOtherSessions();

      expect(apiClient.post).toHaveBeenCalledWith('/auth/sessions/revoke-others/');
    });

    it('gets login history', async () => {
      const mockHistory = [
        { id: '1', timestamp: '2024-01-01', success: true },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockHistory });

      const result = await getLoginHistory();

      expect(apiClient.get).toHaveBeenCalledWith('/auth/login-history/');
      expect(result).toEqual(mockHistory);
    });
  });

  describe('phone verification', () => {
    it('sends phone verification', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await sendPhoneVerification('555-1234');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/phone/verify/send/', {
        phone: '555-1234',
      });
    });

    it('verifies phone code', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await verifyPhoneCode('123456');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/phone/verify/confirm/', {
        code: '123456',
      });
    });
  });

  describe('multiple emails', () => {
    it('gets user emails', async () => {
      const mockEmails = [
        { id: 1, email: 'primary@example.com', is_primary: true, verified: true },
        { id: 2, email: 'secondary@example.com', is_primary: false, verified: false },
      ];
      vi.mocked(apiClient.get).mockResolvedValue({ data: mockEmails });

      const result = await getUserEmails();

      expect(apiClient.get).toHaveBeenCalledWith('/auth/emails/');
      expect(result).toEqual(mockEmails);
    });

    it('adds user email', async () => {
      const mockEmail = { id: 3, email: 'new@example.com', is_primary: false, verified: false };
      vi.mocked(apiClient.post).mockResolvedValue({ data: mockEmail });

      const result = await addUserEmail('new@example.com');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/emails/', { email: 'new@example.com' });
      expect(result).toEqual(mockEmail);
    });

    it('deletes user email', async () => {
      vi.mocked(apiClient.delete).mockResolvedValue({});

      await deleteUserEmail(2);

      expect(apiClient.delete).toHaveBeenCalledWith('/auth/emails/2/');
    });

    it('sends user email verification', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await sendUserEmailVerification(2);

      expect(apiClient.post).toHaveBeenCalledWith('/auth/emails/2/send-verification/');
    });

    it('verifies user email', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await verifyUserEmail(2, 'verify-token');

      expect(apiClient.post).toHaveBeenCalledWith('/auth/emails/2/verify/', {
        token: 'verify-token',
      });
    });

    it('sets primary email', async () => {
      vi.mocked(apiClient.post).mockResolvedValue({});

      await setPrimaryEmail(2);

      expect(apiClient.post).toHaveBeenCalledWith('/auth/emails/2/set-primary/');
    });
  });
});
