import { describe, it, expect, vi, type Mock } from 'vitest';
import {
  GetUnreadNotificationsUseCase,
  type GetUnreadNotificationsInput,
  type GetUnreadNotificationsResult,
} from './GetUnreadNotificationsUseCase';
import type { INotificationRepository } from '../../domain/repositories/INotificationRepository';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Notification } from '../../domain/entities/Notification';

interface NotificationRepositoryMock {
  findUnreadByRecipientId: Mock;
}

interface OutputPortMock extends UseCaseOutputPort<GetUnreadNotificationsResult> {
  present: Mock;
}

describe('GetUnreadNotificationsUseCase', () => {
  let notificationRepository: NotificationRepositoryMock;
  let logger: Logger;
  let output: OutputPortMock;
  let useCase: GetUnreadNotificationsUseCase;

  beforeEach(() => {
    notificationRepository = {
      findUnreadByRecipientId: vi.fn(),
    };

    logger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    } as unknown as Logger;

    output = {
      present: vi.fn(),
    } as unknown as OutputPortMock;

    useCase = new GetUnreadNotificationsUseCase(
      notificationRepository as unknown as INotificationRepository,
      output,
      logger,
    );
  });

  it('returns unread notifications and total count', async () => {
    const recipientId = 'driver-1';
    const notifications: Notification[] = [
      Notification.create({
        id: 'n1',
        recipientId,
        type: 'system_announcement',
        title: 'Test',
        body: 'Body',
        channel: 'in_app',
      }),
    ];

    notificationRepository.findUnreadByRecipientId.mockResolvedValue(notifications);

    const input: GetUnreadNotificationsInput = { recipientId };

    const result = await useCase.execute(input);

    expect(notificationRepository.findUnreadByRecipientId).toHaveBeenCalledWith(recipientId);
    expect(result).toBeInstanceOf(Result);
    expect(result.isOk()).toBe(true);
    expect(output.present).toHaveBeenCalledWith({
      notifications,
      totalCount: 1,
    });
  });

  it('handles repository errors by logging and returning error result', async () => {
    const recipientId = 'driver-1';
    const error = new Error('DB error');
    notificationRepository.findUnreadByRecipientId.mockRejectedValue(error);

    const input: GetUnreadNotificationsInput = { recipientId };

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    const err = result.unwrapErr() as ApplicationErrorCode<'REPOSITORY_ERROR', { message: string }>;
    expect(err.code).toBe('REPOSITORY_ERROR');
    expect(err.details.message).toBe('DB error');
    expect((logger.error as unknown as Mock)).toHaveBeenCalled();
    expect(output.present).not.toHaveBeenCalled();
  });
});
