import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { GetEntitySponsorshipPricingUseCase } from './GetEntitySponsorshipPricingUseCase';
import type { ISponsorshipPricingRepository } from '../../domain/repositories/ISponsorshipPricingRepository';
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
import type { Logger } from '@core/shared/application';

describe('GetEntitySponsorshipPricingUseCase', () => {
  let mockSponsorshipPricingRepo: ISponsorshipPricingRepository;
  let mockSponsorshipRequestRepo: ISponsorshipRequestRepository;
  let mockSeasonSponsorshipRepo: ISeasonSponsorshipRepository;
  let mockLogger: Logger;
  let mockFindByEntity: Mock;
  let mockFindPendingByEntity: Mock;
  let mockFindBySeasonId: Mock;

  beforeEach(() => {
    mockFindByEntity = vi.fn();
    mockFindPendingByEntity = vi.fn();
    mockFindBySeasonId = vi.fn();
    mockSponsorshipPricingRepo = {
      findByEntity: mockFindByEntity,
      findAll: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      save: vi.fn(),
      exists: vi.fn(),
      findAcceptingApplications: vi.fn(),
    } as ISponsorshipPricingRepository;
    mockSponsorshipRequestRepo = {
      findPendingByEntity: mockFindPendingByEntity,
      findByEntity: vi.fn(),
      findById: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      findBySponsorId: vi.fn(),
      findByStatus: vi.fn(),
      findBySponsorIdAndStatus: vi.fn(),
      hasPendingRequest: vi.fn(),
      findPendingBySponsor: vi.fn(),
      findApprovedByEntity: vi.fn(),
      findRejectedByEntity: vi.fn(),
      countPendingByEntity: vi.fn(),
      create: vi.fn(),
      exists: vi.fn(),
    } as ISponsorshipRequestRepository;
    mockSeasonSponsorshipRepo = {
      findBySeasonId: mockFindBySeasonId,
      findById: vi.fn(),
      save: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      findAll: vi.fn(),
      findByLeagueId: vi.fn(),
      findBySponsorId: vi.fn(),
      findBySeasonAndTier: vi.fn(),
      create: vi.fn(),
      exists: vi.fn(),
    } as ISeasonSponsorshipRepository;
    mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
  });

  it('should return null when no pricing found', async () => {
    const useCase = new GetEntitySponsorshipPricingUseCase(
      mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
      mockSponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
      mockSeasonSponsorshipRepo as unknown as ISeasonSponsorshipRepository,
      mockLogger,
    );

    const dto = { entityType: 'season' as const, entityId: 'season1' };

    mockFindByEntity.mockResolvedValue(null);

    const result = await useCase.execute(dto);

    expect(result.isOk()).toBe(true);
    expect(result.value).toBe(null);
  });

  it('should return pricing data when found', async () => {
    const useCase = new GetEntitySponsorshipPricingUseCase(
      mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
      mockSponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
      mockSeasonSponsorshipRepo as unknown as ISeasonSponsorshipRepository,
      mockLogger,
    );

    const dto = { entityType: 'season' as const, entityId: 'season1' };
    const pricing = {
      acceptingApplications: true,
      customRequirements: 'Some requirements',
      mainSlot: {
        price: { amount: 100, currency: 'USD', format: () => '$100' },
        benefits: ['Benefit 1'],
        available: true,
        maxSlots: 5,
      },
      secondarySlots: {
        price: { amount: 50, currency: 'USD', format: () => '$50' },
        benefits: ['Benefit 2'],
        available: true,
        maxSlots: 10,
      },
    };

    mockFindByEntity.mockResolvedValue(pricing);
    mockFindPendingByEntity.mockResolvedValue([]);
    mockFindBySeasonId.mockResolvedValue([]);

    const result = await useCase.execute(dto);

    expect(result.isOk()).toBe(true);
    expect(result.value).toEqual({
      entityType: 'season',
      entityId: 'season1',
      acceptingApplications: true,
      customRequirements: 'Some requirements',
      mainSlot: {
        tier: 'main',
        price: 100,
        currency: 'USD',
        formattedPrice: '$100',
        benefits: ['Benefit 1'],
        available: true,
        maxSlots: 5,
        filledSlots: 0,
        pendingRequests: 0,
      },
      secondarySlot: {
        tier: 'secondary',
        price: 50,
        currency: 'USD',
        formattedPrice: '$50',
        benefits: ['Benefit 2'],
        available: true,
        maxSlots: 10,
        filledSlots: 0,
        pendingRequests: 0,
      },
    });
  });

  it('should return error when repository throws', async () => {
    const useCase = new GetEntitySponsorshipPricingUseCase(
      mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
      mockSponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
      mockSeasonSponsorshipRepo as unknown as ISeasonSponsorshipRepository,
      mockLogger,
    );

    const dto = { entityType: 'season' as const, entityId: 'season1' };
    const error = new Error('Repository error');

    mockFindByEntity.mockRejectedValue(error);

    const result = await useCase.execute(dto);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
    expect(result.unwrapErr().details.message).toBe('Repository error');
  });
});