import { PrismaNotificationRepository } from '../repositories/prisma-notification.repository';

describe('PrismaNotificationRepository', () => {
  let repository: PrismaNotificationRepository;
  let mockPrisma: {
    notificationLog: {
      create: ReturnType<typeof vi.fn>;
      update: ReturnType<typeof vi.fn>;
      findMany: ReturnType<typeof vi.fn>;
    };
  };

  const mockRecord = {
    id: 'notif-1',
    userId: 'user-1',
    channel: 'EMAIL',
    templateKey: 'user.registered',
    subject: 'Welcome',
    body: '<p>Hello</p>',
    metadata: null,
    status: 'PENDING',
    errorDetail: null,
    sentAt: null,
    createdAt: new Date(),
  };

  beforeEach(() => {
    mockPrisma = {
      notificationLog: {
        create: vi.fn(),
        update: vi.fn(),
        findMany: vi.fn(),
      },
    };
    repository = new PrismaNotificationRepository(mockPrisma as any);
  });

  it('create creates notification log with PENDING status and returns entity', async () => {
    mockPrisma.notificationLog.create.mockResolvedValue(mockRecord);

    const result = await repository.create({
      userId: 'user-1',
      channel: 'EMAIL',
      templateKey: 'user.registered',
      subject: 'Welcome',
      body: '<p>Hello</p>',
      metadata: null,
    });

    expect(mockPrisma.notificationLog.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          userId: 'user-1',
          channel: 'EMAIL',
          templateKey: 'user.registered',
          status: 'PENDING',
        }),
      }),
    );
    expect(result.id).toBe('notif-1');
    expect(result.status).toBe('PENDING');
    expect(result.userId).toBe('user-1');
  });

  it('updateStatus updates status to SENT with sentAt date', async () => {
    mockPrisma.notificationLog.update.mockResolvedValue({ ...mockRecord, status: 'SENT', sentAt: new Date() });

    await repository.updateStatus('notif-1', 'SENT');

    expect(mockPrisma.notificationLog.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'notif-1' },
        data: expect.objectContaining({
          status: 'SENT',
          sentAt: expect.any(Date),
        }),
      }),
    );
  });

  it('updateStatus updates status to FAILED with errorDetail', async () => {
    mockPrisma.notificationLog.update.mockResolvedValue({
      ...mockRecord,
      status: 'FAILED',
      errorDetail: 'SMTP connection refused',
    });

    await repository.updateStatus('notif-1', 'FAILED', 'SMTP connection refused');

    expect(mockPrisma.notificationLog.update).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'notif-1' },
        data: expect.objectContaining({
          status: 'FAILED',
          errorDetail: 'SMTP connection refused',
        }),
      }),
    );
  });

  it('findByUserId returns entities ordered by createdAt desc', async () => {
    const records = [
      { ...mockRecord, id: 'notif-2', createdAt: new Date('2024-01-02') },
      { ...mockRecord, id: 'notif-1', createdAt: new Date('2024-01-01') },
    ];
    mockPrisma.notificationLog.findMany.mockResolvedValue(records);

    const result = await repository.findByUserId('user-1');

    expect(mockPrisma.notificationLog.findMany).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { userId: 'user-1' },
        orderBy: { createdAt: 'desc' },
      }),
    );
    expect(result).toHaveLength(2);
    expect(result[0]!.id).toBe('notif-2');
    expect(result[1]!.id).toBe('notif-1');
  });
});
