import { NotFoundException } from '@modules/shared';
import { ForceDeleteUserCommand } from '../commands/force-delete-user/force-delete-user.command';
import { ForceDeleteUserHandler } from '../commands/force-delete-user/force-delete-user.handler';

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

  const mockPrisma = {
    user: { findUnique: vi.fn(), update: vi.fn() },
    refreshToken: { deleteMany: vi.fn() },
    oAuthAccount: { deleteMany: vi.fn() },
    agent: { updateMany: vi.fn() },
    listing: { updateMany: vi.fn() },
    subscription: { updateMany: vi.fn() },
    review: { deleteMany: vi.fn() },
    inquiry: { deleteMany: vi.fn() },
    savedSearch: { deleteMany: vi.fn() },
    notificationLog: { deleteMany: vi.fn() },
    $transaction: vi.fn((fn: (tx: any) => Promise<void>) => fn(mockPrisma)),
  };

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

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

  it('anonymizes user PII and cascades deletion in transaction', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({ id: 'user-1', deletedAt: null });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.oAuthAccount.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.agent.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.listing.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.subscription.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.review.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.inquiry.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.savedSearch.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.notificationLog.deleteMany.mockResolvedValue({ count: 0 });

    const result = await handler.execute(
      new ForceDeleteUserCommand('user-1', 'admin-1', 'violation'),
    );

    expect(result).toEqual({ message: 'Tài khoản đã bị xóa vĩnh viễn' });
    expect(mockPrisma.$transaction).toHaveBeenCalledTimes(1);
    expect(mockPrisma.user.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'user-1' },
        data: expect.objectContaining({
          email: 'deleted_user-1@removed.local',
          fullName: 'Người dùng đã xóa',
          passwordHash: null,
          avatarUrl: null,
          isActive: false,
          deletedAt: expect.any(Date),
          deletionScheduledAt: null,
        }),
      }),
    );
  });

  it('returns early message if user already deleted', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({ id: 'user-1', deletedAt: new Date() });

    const result = await handler.execute(
      new ForceDeleteUserCommand('user-1', 'admin-1', 'cleanup'),
    );

    expect(result).toEqual({ message: 'Tài khoản đã bị xóa' });
    expect(mockPrisma.$transaction).not.toHaveBeenCalled();
  });

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

    await expect(
      handler.execute(new ForceDeleteUserCommand('missing', 'admin-1', 'reason')),
    ).rejects.toThrow(NotFoundException);
  });

  it('expires active listings', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({ id: 'user-1', deletedAt: null });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.oAuthAccount.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.agent.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.listing.updateMany.mockResolvedValue({ count: 2 });
    mockPrisma.subscription.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.review.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.inquiry.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.savedSearch.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.notificationLog.deleteMany.mockResolvedValue({ count: 0 });

    await handler.execute(new ForceDeleteUserCommand('user-1', 'admin-1', 'reason'));

    expect(mockPrisma.listing.updateMany).toHaveBeenCalledWith({
      where: { sellerId: 'user-1', status: { in: ['ACTIVE', 'PENDING_REVIEW'] } },
      data: { status: 'EXPIRED' },
    });
  });

  it('cancels active subscription', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({ id: 'user-1', deletedAt: null });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.oAuthAccount.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.agent.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.listing.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.subscription.updateMany.mockResolvedValue({ count: 1 });
    mockPrisma.review.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.inquiry.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.savedSearch.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.notificationLog.deleteMany.mockResolvedValue({ count: 0 });

    await handler.execute(new ForceDeleteUserCommand('user-1', 'admin-1', 'reason'));

    expect(mockPrisma.subscription.updateMany).toHaveBeenCalledWith({
      where: { userId: 'user-1', status: 'ACTIVE' },
      data: { status: 'CANCELLED', cancelledAt: expect.any(Date) },
    });
  });

  it('deletes reviews, inquiries, saved searches, notifications', async () => {
    mockPrisma.user.findUnique.mockResolvedValue({ id: 'user-1', deletedAt: null });
    mockPrisma.user.update.mockResolvedValue({});
    mockPrisma.refreshToken.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.oAuthAccount.deleteMany.mockResolvedValue({ count: 0 });
    mockPrisma.agent.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.listing.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.subscription.updateMany.mockResolvedValue({ count: 0 });
    mockPrisma.review.deleteMany.mockResolvedValue({ count: 3 });
    mockPrisma.inquiry.deleteMany.mockResolvedValue({ count: 5 });
    mockPrisma.savedSearch.deleteMany.mockResolvedValue({ count: 2 });
    mockPrisma.notificationLog.deleteMany.mockResolvedValue({ count: 10 });

    await handler.execute(new ForceDeleteUserCommand('user-1', 'admin-1', 'reason'));

    expect(mockPrisma.review.deleteMany).toHaveBeenCalledWith({ where: { userId: 'user-1' } });
    expect(mockPrisma.inquiry.deleteMany).toHaveBeenCalledWith({ where: { userId: 'user-1' } });
    expect(mockPrisma.savedSearch.deleteMany).toHaveBeenCalledWith({ where: { userId: 'user-1' } });
    expect(mockPrisma.notificationLog.deleteMany).toHaveBeenCalledWith({ where: { userId: 'user-1' } });
  });
});
