import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { User } from '../../domain/entities/User';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { UserId } from '../../domain/value-objects/UserId';
import { ResetPasswordUseCase } from './ResetPasswordUseCase';

describe('ResetPasswordUseCase', () => {
  let authRepo: {
    findByEmail: Mock;
    save: Mock;
  };
  let magicLinkRepo: {
    findByToken: Mock;
    markAsUsed: Mock;
  };
  let passwordService: {
    hash: Mock;
  };
  let logger: Logger;
  let useCase: ResetPasswordUseCase;

  beforeEach(() => {
    authRepo = {
      findByEmail: vi.fn(),
      save: vi.fn(),
    };

    magicLinkRepo = {
      findByToken: vi.fn(),
      markAsUsed: vi.fn(),
    };

    passwordService = {
      hash: vi.fn(),
    };

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

    useCase = new ResetPasswordUseCase(
      authRepo as unknown as AuthRepository,
      magicLinkRepo as unknown as MagicLinkRepository,
      passwordService as unknown as PasswordHashingService,
      logger,
    );
  });

  it('successfully resets password with valid token', async () => {
    const user = User.create({
      id: UserId.create(),
      displayName: 'John Smith',
      email: 'test@example.com',
      passwordHash: PasswordHash.fromHash('old-hash'),
    });

    const validToken = 'a'.repeat(32); // 32 characters minimum
    magicLinkRepo.findByToken.mockResolvedValue({
      email: 'test@example.com',
      token: validToken,
      expiresAt: new Date(Date.now() + 60000),
      userId: user.getId().value,
      used: false,
    });
    authRepo.findByEmail.mockResolvedValue(user);
    passwordService.hash.mockResolvedValue('new-hashed-password');

    const result = await useCase.execute({
      token: validToken,
      newPassword: 'NewPassword123',
    });

    expect(result.isOk()).toBe(true);
    const resetResult = result.unwrap();
    expect(resetResult.message).toBe('Password reset successfully. You can now log in with your new password.');
    expect(authRepo.save).toHaveBeenCalled();
    expect(magicLinkRepo.markAsUsed).toHaveBeenCalledWith(validToken);
  });

  it('returns error for invalid token', async () => {
    magicLinkRepo.findByToken.mockResolvedValue(null);

    const result = await useCase.execute({
      token: 'invalid-token-that-is-too-short',
      newPassword: 'NewPassword123',
    });

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

  it('returns error for expired token', async () => {
    const expiredToken = 'b'.repeat(32);
    magicLinkRepo.findByToken.mockResolvedValue({
      email: 'test@example.com',
      token: expiredToken,
      expiresAt: new Date(Date.now() - 60000),
      userId: 'user-1',
      used: false,
    });

    const result = await useCase.execute({
      token: expiredToken,
      newPassword: 'NewPassword123',
    });

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

  it('returns error for weak password', async () => {
    const validToken = 'c'.repeat(32);
    magicLinkRepo.findByToken.mockResolvedValue({
      email: 'test@example.com',
      token: validToken,
      expiresAt: new Date(Date.now() + 60000),
      userId: 'user-1',
      used: false,
    });

    const result = await useCase.execute({
      token: validToken,
      newPassword: 'weak',
    });

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

  it('returns error when user no longer exists', async () => {
    const validToken = 'd'.repeat(32);
    magicLinkRepo.findByToken.mockResolvedValue({
      email: 'test@example.com',
      token: validToken,
      expiresAt: new Date(Date.now() + 60000),
      userId: 'user-1',
      used: false,
    });
    authRepo.findByEmail.mockResolvedValue(null);

    const result = await useCase.execute({
      token: validToken,
      newPassword: 'NewPassword123',
    });

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