import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { RejectSponsorshipRequestUseCase } from './RejectSponsorshipRequestUseCase';
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';

describe('RejectSponsorshipRequestUseCase', () => {
  let useCase: RejectSponsorshipRequestUseCase;
  let sponsorshipRequestRepo: { findById: Mock; update: Mock };

  beforeEach(() => {
    sponsorshipRequestRepo = { findById: vi.fn(), update: vi.fn() };
    useCase = new RejectSponsorshipRequestUseCase(
      sponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
    );
  });

  it('should return not found error when request does not exist', async () => {
    sponsorshipRequestRepo.findById.mockResolvedValue(null);

    const result = await useCase.execute({
      requestId: 'request-1',
      respondedBy: 'driver-1',
      reason: 'Not interested',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'SPONSORSHIP_REQUEST_NOT_FOUND',
    });
  });

  it('should return not pending error when request is not pending', async () => {
    const mockRequest = {
      id: 'request-1',
      status: 'accepted',
      isPending: vi.fn().mockReturnValue(false),
    };
    sponsorshipRequestRepo.findById.mockResolvedValue(mockRequest);

    const result = await useCase.execute({
      requestId: 'request-1',
      respondedBy: 'driver-1',
      reason: 'Not interested',
    });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'SPONSORSHIP_REQUEST_NOT_PENDING',
    });
  });

  it('should reject the request successfully', async () => {
    const mockRequest = {
      id: 'request-1',
      status: 'pending',
      isPending: vi.fn().mockReturnValue(true),
      reject: vi.fn().mockReturnValue({
        id: 'request-1',
        respondedAt: new Date('2023-01-01T00:00:00Z'),
        rejectionReason: 'Not interested',
      }),
    };
    sponsorshipRequestRepo.findById.mockResolvedValue(mockRequest);
    sponsorshipRequestRepo.update.mockResolvedValue(undefined);

    const result = await useCase.execute({
      requestId: 'request-1',
      respondedBy: 'driver-1',
      reason: 'Not interested',
    });

    expect(result.isOk()).toBe(true);
    expect(result.unwrap()).toEqual({
      requestId: 'request-1',
      status: 'rejected',
      rejectedAt: new Date('2023-01-01T00:00:00Z'),
      reason: 'Not interested',
    });
    expect(sponsorshipRequestRepo.update).toHaveBeenCalledWith(mockRequest.reject());
  });

  it('should reject the request successfully without reason', async () => {
    const mockRequest = {
      id: 'request-1',
      status: 'pending',
      isPending: vi.fn().mockReturnValue(true),
      reject: vi.fn().mockReturnValue({
        id: 'request-1',
        respondedAt: new Date('2023-01-01T00:00:00Z'),
        rejectionReason: undefined,
      }),
    };
    sponsorshipRequestRepo.findById.mockResolvedValue(mockRequest);
    sponsorshipRequestRepo.update.mockResolvedValue(undefined);

    const result = await useCase.execute({
      requestId: 'request-1',
      respondedBy: 'driver-1',
    });

    expect(result.isOk()).toBe(true);
    expect(result.unwrap()).toEqual({
      requestId: 'request-1',
      status: 'rejected',
      rejectedAt: new Date('2023-01-01T00:00:00Z'),
    });
    expect(sponsorshipRequestRepo.update).toHaveBeenCalledWith(mockRequest.reject());
  });
});