import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { League } from '../../domain/entities/League';
import { Season } from '../../domain/entities/season/Season';
import { SeasonSponsorship } from '../../domain/entities/season/SeasonSponsorship';
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
import { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import { Money } from '../../domain/value-objects/Money';
import {
    GetSponsorDashboardUseCase,
    type GetSponsorDashboardErrorCode,
    type GetSponsorDashboardInput,
} from './GetSponsorDashboardUseCase';

describe('GetSponsorDashboardUseCase', () => {
  let useCase: GetSponsorDashboardUseCase;
  let sponsorRepository: {
    findById: Mock;
  };
  let seasonSponsorshipRepository: {
    findBySponsorId: Mock;
  };
  let seasonRepository: {
    findById: Mock;
  };
  let leagueRepository: {
    findById: Mock;
  };
  let leagueMembershipRepository: {
    getLeagueMembers: Mock;
  };
  let raceRepository: {
    findByLeagueId: Mock;
  };

  beforeEach(() => {
    sponsorRepository = {
      findById: vi.fn(),
    };
    seasonSponsorshipRepository = {
      findBySponsorId: vi.fn(),
    };
    seasonRepository = {
      findById: vi.fn(),
    };
    leagueRepository = {
      findById: vi.fn(),
    };
    leagueMembershipRepository = {
      getLeagueMembers: vi.fn(),
    };
    raceRepository = {
      findByLeagueId: vi.fn(),
    };

    useCase = new GetSponsorDashboardUseCase(
      sponsorRepository as unknown as SponsorRepository,
      seasonSponsorshipRepository as unknown as SeasonSponsorshipRepository,
      seasonRepository as unknown as SeasonRepository,
      leagueRepository as unknown as LeagueRepository,
      leagueMembershipRepository as unknown as LeagueMembershipRepository,
      raceRepository as unknown as RaceRepository,
    );
  });

  it('should return sponsor dashboard for existing sponsor', async () => {
    const sponsorId = 'sponsor-1';
    const sponsor = Sponsor.create({
      id: sponsorId,
      name: 'Test Sponsor',
      contactEmail: 'test@example.com',
    });
    const sponsorship = SeasonSponsorship.create({
      id: 'sponsorship-1',
      sponsorId,
      seasonId: 'season-1',
      tier: 'main',
      pricing: Money.create(10000, 'USD'),
    });
    const season = Season.create({
      id: 'season-1',
      leagueId: 'league-1',
      gameId: 'game-1',
      name: 'Season 1',
      status: 'active',
    });
    const league = League.create({
      id: 'league-1',
      name: 'Test League',
      description: 'Test',
      ownerId: 'owner-1',
    });
    const memberships = [{ driverId: 'driver-1' }];
    const races = [{ id: 'race-1', status: { isCompleted: () => true } }];

    sponsorRepository.findById.mockResolvedValue(sponsor);
    seasonSponsorshipRepository.findBySponsorId.mockResolvedValue([sponsorship]);
    seasonRepository.findById.mockResolvedValue(season);
    leagueRepository.findById.mockResolvedValue(league);
    leagueMembershipRepository.getLeagueMembers.mockResolvedValue(memberships);
    raceRepository.findByLeagueId.mockResolvedValue(races);

    const input: GetSponsorDashboardInput = { sponsorId };
    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    const dashboard = result.unwrap();

    expect(dashboard).toBeDefined();
    expect(dashboard.sponsorId).toBe(sponsorId);
    expect(dashboard.metrics.impressions).toBe(100); // 1 completed race * 1 driver * 100
    expect(dashboard.investment.totalInvestment.amount).toBe(10000);
    expect(dashboard.investment.totalInvestment.currency).toBe('USD');
  });

  it('should return error when sponsor does not exist', async () => {
    const sponsorId = 'sponsor-1';
    sponsorRepository.findById.mockResolvedValue(null);

    const input: GetSponsorDashboardInput = { sponsorId };
    const result = await useCase.execute(input);

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

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

    expect(error.code).toBe('SPONSOR_NOT_FOUND');
    expect(error.details.message).toBe('Sponsor not found');
  });

  it('should return error on repository failure', async () => {
    const sponsorId = 'sponsor-1';
    sponsorRepository.findById.mockRejectedValue(new Error('DB error'));

    const input: GetSponsorDashboardInput = { sponsorId };
    const result = await useCase.execute(input);

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

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

    expect(error.code).toBe('REPOSITORY_ERROR');
    expect(error.details.message).toBe('DB error');
  });
});
