/**
 * Application Use Case: ProcessWalletTransactionUseCase
 *
 * Processes a wallet transaction (deposit or withdrawal).
 */

import type { IWalletRepository, ITransactionRepository } from '../../domain/repositories/IWalletRepository';
import type { Wallet, Transaction, TransactionType, ReferenceType } from '../../domain/entities/Wallet';
import type {
  IProcessWalletTransactionPresenter,
  ProcessWalletTransactionResultDTO,
  ProcessWalletTransactionViewModel,
} from '../presenters/IProcessWalletTransactionPresenter';
import type { UseCase } from '@core/shared/application/UseCase';

export interface ProcessWalletTransactionInput {
  leagueId: string;
  type: TransactionType;
  amount: number;
  description: string;
  referenceId?: string;
  referenceType?: ReferenceType;
}

export class ProcessWalletTransactionUseCase
  implements UseCase<ProcessWalletTransactionInput, ProcessWalletTransactionResultDTO, ProcessWalletTransactionViewModel, IProcessWalletTransactionPresenter>
{
  constructor(
    private readonly walletRepository: IWalletRepository,
    private readonly transactionRepository: ITransactionRepository,
  ) {}

  async execute(
    input: ProcessWalletTransactionInput,
    presenter: IProcessWalletTransactionPresenter,
  ): Promise<void> {
    presenter.reset();

    const { leagueId, type, amount, description, referenceId, referenceType } = input;

    if (!leagueId || !type || amount === undefined || !description) {
      throw new Error('Missing required fields: leagueId, type, amount, description');
    }

    if (type !== ('deposit' as TransactionType) && type !== ('withdrawal' as TransactionType)) {
      throw new Error('Type must be "deposit" or "withdrawal"');
    }

    let wallet = await this.walletRepository.findByLeagueId(leagueId);
    
    if (!wallet) {
      const id = `wallet-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
      const newWallet: Wallet = {
        id,
        leagueId,
        balance: 0,
        totalRevenue: 0,
        totalPlatformFees: 0,
        totalWithdrawn: 0,
        currency: 'USD',
        createdAt: new Date(),
      };
      wallet = await this.walletRepository.create(newWallet);
    }

    if (type === ('withdrawal' as TransactionType)) {
      if (amount > wallet.balance) {
        throw new Error('Insufficient balance');
      }
    }

    const transactionId = `txn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    const transaction: Transaction = {
      id: transactionId,
      walletId: wallet.id,
      type,
      amount,
      description,
      referenceId,
      referenceType,
      createdAt: new Date(),
    };

    const createdTransaction = await this.transactionRepository.create(transaction);

    if (type === ('deposit' as TransactionType)) {
      wallet.balance += amount;
      wallet.totalRevenue += amount;
    } else {
      wallet.balance -= amount;
      wallet.totalWithdrawn += amount;
    }

    const updatedWallet = await this.walletRepository.update(wallet);

    const dto: ProcessWalletTransactionResultDTO = {
      wallet: {
        id: updatedWallet.id,
        leagueId: updatedWallet.leagueId,
        balance: updatedWallet.balance,
        totalRevenue: updatedWallet.totalRevenue,
        totalPlatformFees: updatedWallet.totalPlatformFees,
        totalWithdrawn: updatedWallet.totalWithdrawn,
        currency: updatedWallet.currency,
        createdAt: updatedWallet.createdAt,
      },
      transaction: {
        id: createdTransaction.id,
        walletId: createdTransaction.walletId,
        type: createdTransaction.type,
        amount: createdTransaction.amount,
        description: createdTransaction.description,
        referenceId: createdTransaction.referenceId,
        referenceType: createdTransaction.referenceType,
        createdAt: createdTransaction.createdAt,
      },
    };

    presenter.present(dto);
  }
}