import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { LeagueWallet } from '../../domain/entities/league-wallet/LeagueWallet';
import type { Transaction } from '../../domain/entities/league-wallet/Transaction';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import type { TransactionRepository } from '../../domain/repositories/TransactionRepository';
import { Money } from '../../domain/value-objects/Money';

export type GetLeagueWalletErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'WALLET_NOT_FOUND'
  | 'REPOSITORY_ERROR';

export interface GetLeagueWalletInput {
  leagueId: string;
}

export interface GetLeagueWalletAggregates {
  balance: Money;
  totalRevenue: Money;
  totalFees: Money;
  totalWithdrawals: Money;
  pendingPayouts: Money;
}

export interface GetLeagueWalletResult {
  wallet: LeagueWallet;
  transactions: Transaction[];
  aggregates: GetLeagueWalletAggregates;
}

/**
 * Use Case for retrieving league wallet information.
 */
export class GetLeagueWalletUseCase {
  constructor(
    private readonly leagueRepository: LeagueRepository,
    private readonly leagueWalletRepository: LeagueWalletRepository,
    private readonly transactionRepository: TransactionRepository,
  ) {}

  async execute(
    input: GetLeagueWalletInput,
  ): Promise<
    Result<GetLeagueWalletResult, ApplicationErrorCode<GetLeagueWalletErrorCode, { message: string }>>
  > {
    try {
      const leagueExists = await this.leagueRepository.exists(input.leagueId);
      if (!leagueExists) {
        return Result.err({
          code: 'LEAGUE_NOT_FOUND',
          details: { message: 'League not found' },
        });
      }

      const wallet = await this.leagueWalletRepository.findByLeagueId(input.leagueId);

      if (!wallet) {
        return Result.err({
          code: 'WALLET_NOT_FOUND',
          details: { message: 'League wallet not found' },
        });
      }

      const transactions = await this.transactionRepository.findByWalletId(
        wallet.id.toString(),
      );

      const { aggregates } = this.computeAggregates(wallet.balance, transactions);

      const result: GetLeagueWalletResult = {
        wallet,
        transactions: transactions
          .slice()
          .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()),
        aggregates,
      };

      return Result.ok(result);
    } catch (error) {
      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: {
          message:
            error instanceof Error
              ? error.message
              : 'Failed to fetch league wallet',
        },
      });
    }
  }

  private computeAggregates(
    balance: Money,
    transactions: Transaction[],
  ): { aggregates: GetLeagueWalletAggregates } {
    let totalRevenue = Money.create(0, balance.currency);
    let totalFees = Money.create(0, balance.currency);
    let totalWithdrawals = Money.create(0, balance.currency);
    let pendingPayouts = Money.create(0, balance.currency);

    for (const transaction of transactions) {
      if (
        transaction.type === 'sponsorship_payment' ||
        transaction.type === 'membership_payment' ||
        transaction.type === 'prize_payout'
      ) {
        totalRevenue = totalRevenue.add(transaction.amount);
        totalFees = totalFees.add(transaction.platformFee);
      }

      if (transaction.type === 'withdrawal' && transaction.status === 'completed') {
        totalWithdrawals = totalWithdrawals.add(transaction.netAmount);
      }

      if (transaction.type === 'prize_payout' && transaction.status === 'pending') {
        pendingPayouts = pendingPayouts.add(transaction.netAmount);
      }
    }

    return {
      aggregates: {
        balance,
        totalRevenue,
        totalFees,
        totalWithdrawals,
        pendingPayouts,
      },
    };
  }
}