import { NotFoundException } from '@modules/shared';
import { ExportUserDataCommand } from '../commands/export-user-data/export-user-data.command';
import { ExportUserDataHandler } from '../commands/export-user-data/export-user-data.handler';

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

  const mockPrisma = {
    user: { findUnique: vi.fn() },
    agent: { findUnique: vi.fn() },
    listing: { findMany: vi.fn() },
    payment: { findMany: vi.fn() },
    subscription: { findFirst: vi.fn() },
    review: { findMany: vi.fn() },
    inquiry: { findMany: vi.fn() },
    savedSearch: { findMany: vi.fn() },
    transaction: { findMany: vi.fn() },
  };

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

  const sampleUser = {
    id: 'user-1',
    email: 'test@example.com',
    phone: '0912345678',
    fullName: 'Nguyen Van A',
    role: 'BUYER',
    kycStatus: 'VERIFIED',
    createdAt: new Date('2025-01-01'),
  };

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

  it('exports all user data including relations', async () => {
    mockPrisma.user.findUnique.mockResolvedValue(sampleUser);
    mockPrisma.agent.findUnique.mockResolvedValue({ id: 'agent-1', userId: 'user-1' });
    mockPrisma.listing.findMany.mockResolvedValue([{ id: 'listing-1' }]);
    mockPrisma.payment.findMany.mockResolvedValue([{ id: 'pay-1', amountVND: 500000 }]);
    mockPrisma.subscription.findFirst.mockResolvedValue({ id: 'sub-1', status: 'ACTIVE' });
    mockPrisma.review.findMany.mockResolvedValue([{ id: 'review-1' }]);
    mockPrisma.inquiry.findMany.mockResolvedValue([{ id: 'inquiry-1' }]);
    mockPrisma.savedSearch.findMany.mockResolvedValue([{ id: 'ss-1' }]);
    mockPrisma.transaction.findMany.mockResolvedValue([{ id: 'tx-1' }]);

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

    expect(result.user).toEqual(sampleUser);
    expect(result.agent).toEqual({ id: 'agent-1', userId: 'user-1' });
    expect(result.listings).toHaveLength(1);
    expect(result.payments).toHaveLength(1);
    expect(result.subscription).toEqual({ id: 'sub-1', status: 'ACTIVE' });
    expect(result.reviews).toHaveLength(1);
    expect(result.inquiries).toHaveLength(1);
    expect(result.savedSearches).toHaveLength(1);
    expect(result.transactions).toHaveLength(1);
  });

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

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

  it('includes exportedAt timestamp', async () => {
    mockPrisma.user.findUnique.mockResolvedValue(sampleUser);
    mockPrisma.agent.findUnique.mockResolvedValue(null);
    mockPrisma.listing.findMany.mockResolvedValue([]);
    mockPrisma.payment.findMany.mockResolvedValue([]);
    mockPrisma.subscription.findFirst.mockResolvedValue(null);
    mockPrisma.review.findMany.mockResolvedValue([]);
    mockPrisma.inquiry.findMany.mockResolvedValue([]);
    mockPrisma.savedSearch.findMany.mockResolvedValue([]);
    mockPrisma.transaction.findMany.mockResolvedValue([]);

    const before = new Date().toISOString();
    const result = await handler.execute(new ExportUserDataCommand('user-1'));
    const after = new Date().toISOString();

    expect(result.exportedAt).toBeDefined();
    expect(result.exportedAt >= before).toBe(true);
    expect(result.exportedAt <= after).toBe(true);
  });
});
