import { describe, expect, it, vi } from 'vitest';

import { TypeOrmNotificationRepository } from './TypeOrmNotificationRepository';

describe('TypeOrmNotificationRepository', () => {
  it('does not construct its own mapper dependencies', () => {
    // Check that the repository doesn't create its own mapper
    const sourcePath = require.resolve('./TypeOrmNotificationRepository.ts');
    const fs = require('fs');
    const source = fs.readFileSync(sourcePath, 'utf8');

    expect(source).not.toMatch(/new\s+NotificationOrmMapper\s*\(/);
    expect(source).not.toMatch(/=\s*new\s+NotificationOrmMapper\s*\(/);
  });

  it('requires mapper injection via constructor (no default mapper)', () => {
    expect(TypeOrmNotificationRepository.length).toBe(2);
  });

  it('uses the injected mapper at runtime (DB-free)', async () => {
    const ormRepo = {
      findOne: vi.fn().mockResolvedValue({ id: 'notification-1' }),
      find: vi.fn().mockResolvedValue([{ id: 'notification-1' }, { id: 'notification-2' }]),
      save: vi.fn().mockResolvedValue({ id: 'notification-1' }),
      delete: vi.fn().mockResolvedValue({ affected: 1 }),
      update: vi.fn().mockResolvedValue({ affected: 1 }),
      count: vi.fn().mockResolvedValue(1),
    };

    const dataSource = {
      getRepository: vi.fn().mockReturnValue(ormRepo),
    };

    const mapper = {
      toDomain: vi.fn().mockReturnValue({ id: 'domain-notification-1' }),
      toOrmEntity: vi.fn().mockReturnValue({ id: 'orm-notification-1' }),
    };

    const repo = new TypeOrmNotificationRepository(dataSource as any, mapper as any);

    // Test findById
    const notification = await repo.findById('notification-1');
    expect(dataSource.getRepository).toHaveBeenCalledTimes(1);
    expect(ormRepo.findOne).toHaveBeenCalledWith({ where: { id: 'notification-1' } });
    expect(mapper.toDomain).toHaveBeenCalledTimes(1);
    expect(notification).toEqual({ id: 'domain-notification-1' });

    // Test findByRecipientId
    const notifications = await repo.findByRecipientId('driver-123');
    expect(ormRepo.find).toHaveBeenCalledWith({ where: { recipientId: 'driver-123' }, order: { createdAt: 'DESC' } });
    expect(mapper.toDomain).toHaveBeenCalledTimes(3); // 1 from findById + 2 from findByRecipientId
    expect(notifications).toHaveLength(2);

    // Test findUnreadByRecipientId
    await repo.findUnreadByRecipientId('driver-123');
    expect(ormRepo.find).toHaveBeenCalledWith({ where: { recipientId: 'driver-123', status: 'unread' }, order: { createdAt: 'DESC' } });

    // Test countUnreadByRecipientId
    const count = await repo.countUnreadByRecipientId('driver-123');
    expect(ormRepo.count).toHaveBeenCalledWith({ where: { recipientId: 'driver-123', status: 'unread' } });
    expect(count).toBe(1);

    // Test create
    const domainNotification = { id: 'new-notification', toJSON: () => ({ id: 'new-notification' }) };
    await repo.create(domainNotification as any);
    expect(mapper.toOrmEntity).toHaveBeenCalledWith(domainNotification);
    expect(ormRepo.save).toHaveBeenCalledWith({ id: 'orm-notification-1' });

    // Test update
    await repo.update(domainNotification as any);
    expect(mapper.toOrmEntity).toHaveBeenCalledWith(domainNotification);
    expect(ormRepo.save).toHaveBeenCalledWith({ id: 'orm-notification-1' });

    // Test delete
    await repo.delete('notification-1');
    expect(ormRepo.delete).toHaveBeenCalledWith({ id: 'notification-1' });

    // Test deleteAllByRecipientId
    await repo.deleteAllByRecipientId('driver-123');
    expect(ormRepo.delete).toHaveBeenCalledWith({ recipientId: 'driver-123' });

    // Test markAllAsReadByRecipientId
    await repo.markAllAsReadByRecipientId('driver-123');
    expect(ormRepo.update).toHaveBeenCalledWith(
      { recipientId: 'driver-123', status: 'unread' },
      { status: 'read', readAt: expect.any(Date) },
    );
  });
});