import { NotFoundException, ValidationException } from '@modules/shared';
import { RequestUserDeletionCommand } from '../commands/request-user-deletion/request-user-deletion.command';
import { RequestUserDeletionHandler } from '../commands/request-user-deletion/request-user-deletion.handler';

describe('RequestUserDeletionHandler', () => {
  let handler: RequestUserDeletionHandler;

  const mockPrisma = {
    user: { findUnique: vi.fn(), update: vi.fn() },
    refreshToken: { updateMany: vi.fn() },
  };

  const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };

  beforeEach(() => {
    vi.clearAllMocks();
    handler = new RequestUserDeletionHandler(mockPrisma as any, mockLogger as any);
  });

  it('schedules deletion 30 days from now and deactivates user', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      deletedAt: null,
      deletionScheduledAt: null,
    });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.updateMany.mockResolvedValue({ count: 2 });

    const result = await handler.execute(new RequestUserDeletionCommand('user-1'));

    expect(result.scheduledAt).toBeInstanceOf(Date);
    const diffDays = Math.round(
      (result.scheduledAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24),
    );
    expect(diffDays).toBe(30);

    expect(mockPrisma.user.update).toHaveBeenCalledWith({
      where: { id: 'user-1' },
      data: { deletionScheduledAt: expect.any(Date), isActive: false },
    });
  });

  it('revokes all active refresh tokens', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      deletedAt: null,
      deletionScheduledAt: null,
    });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.updateMany.mockResolvedValue({ count: 3 });

    await handler.execute(new RequestUserDeletionCommand('user-1'));

    expect(mockPrisma.refreshToken.updateMany).toHaveBeenCalledWith({
      where: { userId: 'user-1', revokedAt: null },
      data: { revokedAt: expect.any(Date) },
    });
  });

  it('throws NotFoundException if user not found', async () => {
    mockPrisma.user.findUnique.mockResolvedValue(null);

    await expect(
      handler.execute(new RequestUserDeletionCommand('missing')),
    ).rejects.toThrow(NotFoundException);
  });

  it('throws ValidationException if user already deleted', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      deletedAt: new Date(),
      deletionScheduledAt: null,
    });

    await expect(
      handler.execute(new RequestUserDeletionCommand('user-1')),
    ).rejects.toThrow(ValidationException);
  });

  it('throws ValidationException if deletion already scheduled', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({
      id: 'user-1',
      deletedAt: null,
      deletionScheduledAt: new Date(),
    });

    await expect(
      handler.execute(new RequestUserDeletionCommand('user-1')),
    ).rejects.toThrow(ValidationException);
  });
});
