/**
 * Application Use Case: GetSponsorDashboardUseCase
 *
 * Returns sponsor dashboard metrics including sponsorships, impressions, and investment data.
 */

import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository';
import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import { Money } from '../../domain/value-objects/Money';

export interface GetSponsorDashboardInput {
  sponsorId: string;
}

export interface SponsoredLeagueMetrics {
  drivers: number;
  races: number;
  impressions: number;
}

export type SponsoredLeagueStatus = 'active' | 'upcoming' | 'completed';

export interface SponsoredLeagueSummary {
  leagueId: string;
  leagueName: string;
  tier: 'main' | 'secondary';
  metrics: SponsoredLeagueMetrics;
  status: SponsoredLeagueStatus;
}

export interface SponsorDashboardMetrics {
  impressions: number;
  impressionsChange: number;
  uniqueViewers: number;
  viewersChange: number;
  races: number;
  drivers: number;
  exposure: number;
  exposureChange: number;
}

export interface SponsorInvestmentSummary {
  activeSponsorships: number;
  totalInvestment: Money;
  costPerThousandViews: number;
}

export interface GetSponsorDashboardResult {
  sponsorId: string;
  sponsorName: string;
  metrics: SponsorDashboardMetrics;
  sponsoredLeagues: SponsoredLeagueSummary[];
  investment: SponsorInvestmentSummary;
}

export type GetSponsorDashboardErrorCode = 'SPONSOR_NOT_FOUND' | 'REPOSITORY_ERROR';

export class GetSponsorDashboardUseCase {
  constructor(
    private readonly sponsorRepository: ISponsorRepository,
    private readonly seasonSponsorshipRepository: ISeasonSponsorshipRepository,
    private readonly seasonRepository: ISeasonRepository,
    private readonly leagueRepository: ILeagueRepository,
    private readonly leagueMembershipRepository: ILeagueMembershipRepository,
    private readonly raceRepository: IRaceRepository,
    private readonly output: UseCaseOutputPort<GetSponsorDashboardResult>,
  ) {}

  async execute(
    params: GetSponsorDashboardInput,
  ): Promise<Result<void, ApplicationErrorCode<GetSponsorDashboardErrorCode, { message: string }>>> {
    try {
      const { sponsorId } = params;

      const sponsor = await this.sponsorRepository.findById(sponsorId);
      if (!sponsor) {
        return Result.err({
          code: 'SPONSOR_NOT_FOUND',
          details: {
            message: 'Sponsor not found',
          },
        });
      }

      // Get all sponsorships for this sponsor
      const sponsorships = await this.seasonSponsorshipRepository.findBySponsorId(sponsorId);

      // Aggregate data across all sponsorships
      let totalImpressions = 0;
      let totalDrivers = 0;
      let totalRaces = 0;
      let totalInvestmentMoney = Money.create(0, 'USD');
      const sponsoredLeagues: SponsoredLeagueSummary[] = [];
      const seenLeagues = new Set<string>();

      for (const sponsorship of sponsorships) {
        // Get season to find league
        const season = await this.seasonRepository.findById(sponsorship.seasonId);
        if (!season) continue;

        // Only process each league once
        if (seenLeagues.has(season.leagueId)) continue;
        seenLeagues.add(season.leagueId);

        const league = await this.leagueRepository.findById(season.leagueId);
        if (!league) continue;

        // Get membership count for this league
        const memberships = await this.leagueMembershipRepository.getLeagueMembers(season.leagueId);
        const driverCount = memberships.length;
        totalDrivers += driverCount;

        // Get races for this league
        const races = await this.raceRepository.findByLeagueId(season.leagueId);
        const raceCount = races.length;
        totalRaces += raceCount;

        // Calculate impressions based on completed races and drivers
        const completedRaces = races.filter(r => r.status.isCompleted()).length;
        const leagueImpressions = completedRaces * driverCount * 100; // Simplified: 100 views per driver per race
        totalImpressions += leagueImpressions;

        // Determine status based on season dates
        const now = new Date();
        let status: SponsoredLeagueStatus = 'active';
        if (season.endDate && season.endDate < now) {
          status = 'completed';
        } else if (season.startDate && season.startDate > now) {
          status = 'upcoming';
        }

        // Add investment
        totalInvestmentMoney = totalInvestmentMoney.add(
          Money.create(sponsorship.pricing.amount, sponsorship.pricing.currency),
        );

        sponsoredLeagues.push({
          leagueId: league.id.toString(),
          leagueName: league.name.toString(),
          tier: sponsorship.tier,
          metrics: {
            drivers: driverCount,
            races: raceCount,
            impressions: leagueImpressions,
          },
          status,
        });
      }

      const activeSponsorships = sponsorships.filter((s) => s.status.toString() === 'active').length;
      const costPerThousandViews = totalImpressions > 0
        ? totalInvestmentMoney.amount / (totalImpressions / 1000)
        : 0;

      // Calculate unique viewers (simplified: assume 70% of impressions are unique)
      const uniqueViewers = Math.round(totalImpressions * 0.7);

      // Calculate exposure score (0-100 based on tier distribution)
      const mainSponsorships = sponsorships.filter(s => s.tier === 'main').length;
      const exposure = sponsorships.length > 0
        ? Math.min(100, (mainSponsorships * 30) + (sponsorships.length * 10))
        : 0;

      const result: GetSponsorDashboardResult = {
        sponsorId,
        sponsorName: sponsor.name.toString(),
        metrics: {
          impressions: totalImpressions,
          impressionsChange: 0,
          uniqueViewers,
          viewersChange: 0,
          races: totalRaces,
          drivers: totalDrivers,
          exposure,
          exposureChange: 0,
        },
        sponsoredLeagues,
        investment: {
          activeSponsorships,
          totalInvestment: totalInvestmentMoney,
          costPerThousandViews: Math.round(costPerThousandViews * 100) / 100,
        },
      };

      this.output.present(result);

      return Result.ok(undefined);
    } catch (err) {
      const error = err as { message?: string } | undefined;

      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: {
          message: error?.message ?? 'Failed to fetch sponsor dashboard',
        },
      });
    }
  }
}
