/**
 * @ai-summary Unit tests for AuthService
 * @ai-context Tests business logic with mocked dependencies
 */

import { AuthService } from '../../domain/auth.service';
import { UserProfileRepository } from '../../../user-profile/data/user-profile.repository';
import { auth0ManagementClient } from '../../../../core/auth/auth0-management.client';

// Mock dependencies
jest.mock('../../../user-profile/data/user-profile.repository');
jest.mock('../../../../core/auth/auth0-management.client');

const mockUserProfileRepository = jest.mocked(UserProfileRepository);
const mockAuth0Client = jest.mocked(auth0ManagementClient);

describe('AuthService', () => {
  let service: AuthService;
  let repositoryInstance: jest.Mocked<UserProfileRepository>;

  beforeEach(() => {
    jest.clearAllMocks();

    repositoryInstance = {
      create: jest.fn(),
      getByAuth0Sub: jest.fn(),
      updateEmailVerified: jest.fn(),
    } as any;

    mockUserProfileRepository.mockImplementation(() => repositoryInstance);
    service = new AuthService(repositoryInstance);
  });

  describe('signup', () => {
    it('creates user in Auth0 and local database successfully', async () => {
      const email = 'test@example.com';
      const password = 'Password123';
      const auth0UserId = 'auth0|123456';

      mockAuth0Client.createUser.mockResolvedValue(auth0UserId);
      repositoryInstance.create.mockResolvedValue({
        id: 'user-uuid',
        auth0Sub: auth0UserId,
        email,
        subscriptionTier: 'free',
        emailVerified: false,
        onboardingCompletedAt: null,
        deactivatedAt: null,
        deactivatedBy: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      const result = await service.signup({ email, password });

      expect(mockAuth0Client.createUser).toHaveBeenCalledWith({ email, password });
      expect(repositoryInstance.create).toHaveBeenCalledWith(auth0UserId, email, undefined);
      expect(result).toEqual({
        userId: auth0UserId,
        email,
        message: 'Account created successfully. Please check your email to verify your account.',
      });
    });

    it('throws error when email already exists in Auth0', async () => {
      const email = 'existing@example.com';
      const password = 'Password123';

      mockAuth0Client.createUser.mockRejectedValue(
        new Error('User already exists')
      );

      await expect(service.signup({ email, password })).rejects.toThrow('Email already exists');
    });

    it('propagates other Auth0 errors', async () => {
      const email = 'test@example.com';
      const password = 'Password123';

      mockAuth0Client.createUser.mockRejectedValue(new Error('Auth0 API error'));

      await expect(service.signup({ email, password })).rejects.toThrow('Auth0 API error');
    });
  });

  describe('getVerifyStatus', () => {
    it('returns verification status from Auth0 and updates local database', async () => {
      const auth0Sub = 'auth0|123456';
      const email = 'test@example.com';

      mockAuth0Client.getUser.mockResolvedValue({
        userId: auth0Sub,
        email,
        emailVerified: true,
      });

      repositoryInstance.getByAuth0Sub.mockResolvedValue({
        id: 'user-uuid',
        auth0Sub,
        email,
        subscriptionTier: 'free',
        emailVerified: false,
        onboardingCompletedAt: null,
        deactivatedAt: null,
        deactivatedBy: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      repositoryInstance.updateEmailVerified.mockResolvedValue({
        id: 'user-uuid',
        auth0Sub,
        email,
        subscriptionTier: 'free',
        emailVerified: true,
        onboardingCompletedAt: null,
        deactivatedAt: null,
        deactivatedBy: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      const result = await service.getVerifyStatus(auth0Sub);

      expect(mockAuth0Client.getUser).toHaveBeenCalledWith(auth0Sub);
      expect(repositoryInstance.updateEmailVerified).toHaveBeenCalledWith(auth0Sub, true);
      expect(result).toEqual({
        emailVerified: true,
        email,
      });
    });

    it('does not update local database if verification status unchanged', async () => {
      const auth0Sub = 'auth0|123456';
      const email = 'test@example.com';

      mockAuth0Client.getUser.mockResolvedValue({
        userId: auth0Sub,
        email,
        emailVerified: false,
      });

      repositoryInstance.getByAuth0Sub.mockResolvedValue({
        id: 'user-uuid',
        auth0Sub,
        email,
        subscriptionTier: 'free',
        emailVerified: false,
        onboardingCompletedAt: null,
        deactivatedAt: null,
        deactivatedBy: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      });

      const result = await service.getVerifyStatus(auth0Sub);

      expect(mockAuth0Client.getUser).toHaveBeenCalledWith(auth0Sub);
      expect(repositoryInstance.updateEmailVerified).not.toHaveBeenCalled();
      expect(result).toEqual({
        emailVerified: false,
        email,
      });
    });
  });

  describe('resendVerification', () => {
    it('sends verification email when user is not verified', async () => {
      const auth0Sub = 'auth0|123456';

      mockAuth0Client.checkEmailVerified.mockResolvedValue(false);
      mockAuth0Client.resendVerificationEmail.mockResolvedValue(undefined);

      const result = await service.resendVerification(auth0Sub);

      expect(mockAuth0Client.checkEmailVerified).toHaveBeenCalledWith(auth0Sub);
      expect(mockAuth0Client.resendVerificationEmail).toHaveBeenCalledWith(auth0Sub);
      expect(result).toEqual({
        message: 'Verification email sent. Please check your inbox.',
      });
    });

    it('skips sending email when user is already verified', async () => {
      const auth0Sub = 'auth0|123456';

      mockAuth0Client.checkEmailVerified.mockResolvedValue(true);

      const result = await service.resendVerification(auth0Sub);

      expect(mockAuth0Client.checkEmailVerified).toHaveBeenCalledWith(auth0Sub);
      expect(mockAuth0Client.resendVerificationEmail).not.toHaveBeenCalled();
      expect(result).toEqual({
        message: 'Email is already verified',
      });
    });

    it('propagates Auth0 errors', async () => {
      const auth0Sub = 'auth0|123456';

      mockAuth0Client.checkEmailVerified.mockRejectedValue(new Error('Auth0 API error'));

      await expect(service.resendVerification(auth0Sub)).rejects.toThrow('Auth0 API error');
    });
  });
});
