import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import type { UserRepository } from '../../domain/repositories/UserRepository';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import { SignupWithEmailUseCase } from './SignupWithEmailUseCase';

describe('SignupWithEmailUseCase', () => {
  let userRepository: {
    findByEmail: Mock;
    create: Mock;
  };
  let sessionPort: {
    createSession: Mock;
  };
  let logger: Logger;
  let useCase: SignupWithEmailUseCase;

  beforeEach(() => {
    userRepository = {
      findByEmail: vi.fn(),
      create: vi.fn(),
    };

    sessionPort = {
      createSession: vi.fn(),
    };

    logger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    } as unknown as Logger;

    useCase = new SignupWithEmailUseCase(
      userRepository as unknown as UserRepository,
      sessionPort as unknown as IdentitySessionPort,
      logger,
    );
  });

  it('creates a new user and session for valid input', async () => {
    userRepository.findByEmail.mockResolvedValue(null);
    userRepository.create.mockResolvedValue(undefined);
    sessionPort.createSession.mockResolvedValue({
      token: 'session-token',
      user: {
        id: 'user-1',
        displayName: 'Test User',
        email: 'test@example.com',
      },
      issuedAt: Date.now(),
      expiresAt: Date.now() + 1000,
    });

    const result = await useCase.execute({
      email: 'test@example.com',
      password: 'Password123',
      displayName: 'Test User',
    });

    expect(result.isOk()).toBe(true);
    const signupResult = result.unwrap();
    expect(signupResult.sessionToken).toBe('session-token');
    expect(signupResult.userId).toBe('user-1');
    expect(signupResult.displayName).toBe('Test User');
    expect(signupResult.email).toBe('test@example.com');
    expect(signupResult.isNewUser).toBe(true);
    expect(userRepository.findByEmail).toHaveBeenCalledWith('test@example.com');
    expect(userRepository.create).toHaveBeenCalled();
    expect(sessionPort.createSession).toHaveBeenCalled();
  });

  it('returns error for invalid email format', async () => {
    const result = await useCase.execute({
      email: 'invalid-email',
      password: 'Password123',
      displayName: 'Test User',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('INVALID_EMAIL_FORMAT');
  });

  it('returns error for weak password', async () => {
    const result = await useCase.execute({
      email: 'test@example.com',
      password: 'weak',
      displayName: 'Test User',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('WEAK_PASSWORD');
  });

  it('returns error for invalid display name', async () => {
    const result = await useCase.execute({
      email: 'test@example.com',
      password: 'Password123',
      displayName: 'A',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('INVALID_DISPLAY_NAME');
  });

  it('returns error when email already exists', async () => {
    userRepository.findByEmail.mockResolvedValue({
      id: 'existing-user',
      email: 'test@example.com',
      displayName: 'Existing User',
      passwordHash: 'hash',
      createdAt: new Date(),
    });

    const result = await useCase.execute({
      email: 'test@example.com',
      password: 'Password123',
      displayName: 'Test User',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('EMAIL_ALREADY_EXISTS');
  });
});