import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { AcceptSponsorshipRequestUseCase } from './AcceptSponsorshipRequestUseCase';
import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository';
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { INotificationService } from '@core/notifications/application/ports/INotificationService';
import type { IPaymentGateway } from '../ports/IPaymentGateway';
import type { IWalletRepository } from '@core/payments/domain/repositories/IWalletRepository';
import type { ILeagueWalletRepository } from '../../domain/repositories/ILeagueWalletRepository';
import type { Logger } from '@core/shared/application';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import { Season } from '../../domain/entities/Season';
import { LeagueWallet } from '../../domain/entities/LeagueWallet';
import { Money } from '../../domain/value-objects/Money';

describe('AcceptSponsorshipRequestUseCase', () => {
  let mockSponsorshipRequestRepo: {
    findById: Mock;
    update: Mock;
  };
  let mockSeasonSponsorshipRepo: {
    create: Mock;
  };
  let mockSeasonRepo: {
    findById: Mock;
  };
  let mockNotificationService: {
    sendNotification: Mock;
  };
  let mockPaymentGateway: {
    processPayment: Mock;
  };
  let mockWalletRepo: {
    findById: Mock;
    update: Mock;
  };
  let mockLeagueWalletRepo: {
    findById: Mock;
    update: Mock;
  };
  let mockLogger: {
    debug: Mock;
    info: Mock;
    warn: Mock;
    error: Mock;
  };

  beforeEach(() => {
    mockSponsorshipRequestRepo = {
      findById: vi.fn(),
      update: vi.fn(),
    };
    mockSeasonSponsorshipRepo = {
      create: vi.fn(),
    };
    mockSeasonRepo = {
      findById: vi.fn(),
    };
    mockNotificationService = {
      sendNotification: vi.fn(),
    };
    mockPaymentGateway = {
      processPayment: vi.fn(),
    };
    mockWalletRepo = {
      findById: vi.fn(),
      update: vi.fn(),
    };
    mockLeagueWalletRepo = {
      findById: vi.fn(),
      update: vi.fn(),
    };
    mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
  });

  it('should send notification to sponsor, process payment, and update wallets when accepting season sponsorship', async () => {
    const useCase = new AcceptSponsorshipRequestUseCase(
      mockSponsorshipRequestRepo as unknown as ISponsorshipRequestRepository,
      mockSeasonSponsorshipRepo as unknown as ISeasonSponsorshipRepository,
      mockSeasonRepo as unknown as ISeasonRepository,
      mockNotificationService as unknown as INotificationService,
      mockPaymentGateway as unknown as IPaymentGateway,
      mockWalletRepo as unknown as IWalletRepository,
      mockLeagueWalletRepo as unknown as ILeagueWalletRepository,
      mockLogger as unknown as Logger,
    );

    const request = SponsorshipRequest.create({
      id: 'req1',
      sponsorId: 'sponsor1',
      entityId: 'season1',
      entityType: 'season',
      tier: 'main',
      offeredAmount: Money.create(1000),
      status: 'pending',
    });

    const season = Season.create({
      id: 'season1',
      leagueId: 'league1',
      gameId: 'game1',
      name: 'Season 1',
      startDate: new Date(),
      endDate: new Date(),
    });

    mockSponsorshipRequestRepo.findById.mockResolvedValue(request);
    mockSeasonRepo.findById.mockResolvedValue(season);
    mockNotificationService.sendNotification.mockResolvedValue(undefined);
    mockPaymentGateway.processPayment.mockResolvedValue({
      success: true,
      transactionId: 'txn1',
      timestamp: new Date(),
    });
    mockWalletRepo.findById.mockResolvedValue({
      id: 'sponsor1',
      leagueId: 'league1',
      balance: 2000,
      totalRevenue: 0,
      totalPlatformFees: 0,
      totalWithdrawn: 0,
      currency: 'USD',
      createdAt: new Date(),
    });
    const leagueWallet = LeagueWallet.create({
      id: 'league1',
      leagueId: 'league1',
      balance: Money.create(500),
    });
    mockLeagueWalletRepo.findById.mockResolvedValue(leagueWallet);

    const result = await useCase.execute({
      requestId: 'req1',
      respondedBy: 'driver1',
    });

    expect(result.isOk()).toBe(true);
    const dto = result.unwrap();
    expect(dto).toBeDefined();
    expect(mockNotificationService.sendNotification).toHaveBeenCalledWith({
      recipientId: 'sponsor1',
      type: 'sponsorship_request_accepted',
      title: 'Sponsorship Accepted',
      body: 'Your sponsorship request for Season 1 has been accepted.',
      channel: 'in_app',
      urgency: 'toast',
      data: {
        requestId: 'req1',
        sponsorshipId: dto.sponsorshipId,
      },
    });
    expect(mockPaymentGateway.processPayment).toHaveBeenCalledWith(
      Money.create(1000),
      'sponsor1',
      'Sponsorship payment for season season1',
      { requestId: 'req1' }
    );
    expect(mockWalletRepo.update).toHaveBeenCalledWith(
      expect.objectContaining({
        id: 'sponsor1',
        balance: 1000,
      })
    );
    expect(mockLeagueWalletRepo.update).toHaveBeenCalledWith(
      expect.objectContaining({
        id: 'league1',
        balance: expect.objectContaining({ amount: 1400 }),
      })
    );
  });
});