import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import {
  GetSponsorUseCase,
  type GetSponsorInput,
  type GetSponsorResult,
  type GetSponsorErrorCode,
} from './GetSponsorUseCase';
import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository';
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

describe('GetSponsorUseCase', () => {
  let sponsorRepository: {
    findById: Mock;
  };

  let output: UseCaseOutputPort<GetSponsorResult> & { present: Mock };

  let useCase: GetSponsorUseCase;

  beforeEach(() => {
    sponsorRepository = {
      findById: vi.fn(),
    };

    output = {
      present: vi.fn(),
    } as unknown as UseCaseOutputPort<GetSponsorResult> & { present: Mock };

    useCase = new GetSponsorUseCase(
      sponsorRepository as unknown as ISponsorRepository,
      output,
    );
  });

  it('presents sponsor when found', async () => {
    const sponsor = Sponsor.create({
      id: 'sponsor-1',
      name: 'Test Sponsor',
      contactEmail: 'test@example.com',
    });

    sponsorRepository.findById.mockResolvedValue(sponsor);

    const input: GetSponsorInput = { sponsorId: 'sponsor-1' };
    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(output.present).toHaveBeenCalledWith({ sponsor });
  });

  it('returns SPONSOR_NOT_FOUND when sponsor does not exist', async () => {
    sponsorRepository.findById.mockResolvedValue(null);

    const input: GetSponsorInput = { sponsorId: 'sponsor-404' };
    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);

    const err = result.unwrapErr() as ApplicationErrorCode<
      GetSponsorErrorCode,
      { message: string }
    >;

    expect(err.code).toBe('SPONSOR_NOT_FOUND');
    expect(err.details.message).toBe('Sponsor not found');
    expect(output.present).not.toHaveBeenCalled();
  });

  it('returns REPOSITORY_ERROR when repository throws', async () => {
    sponsorRepository.findById.mockRejectedValue(new Error('DB error'));

    const input: GetSponsorInput = { sponsorId: 'sponsor-1' };
    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);

    const err = result.unwrapErr() as ApplicationErrorCode<
      GetSponsorErrorCode,
      { message: string }
    >;

    expect(err.code).toBe('REPOSITORY_ERROR');
    expect(err.details.message).toBe('DB error');
    expect(output.present).not.toHaveBeenCalled();
  });
});