/**
 * Domain Entity: LeagueWallet
 *
 * Represents a league's financial wallet.
 * Aggregate root for managing league finances and transactions.
 */

import { Entity } from '@core/shared/domain/Entity';
import { RacingDomainInvariantError, RacingDomainValidationError } from '../../errors/RacingDomainError';

import type { Money } from '../../value-objects/Money';
import { LeagueId } from '../LeagueId';
import { LeagueWalletId } from './LeagueWalletId';
import { TransactionId } from './TransactionId';

export interface LeagueWalletProps {
  id: LeagueWalletId;
  leagueId: LeagueId;
  balance: Money;
  transactionIds: TransactionId[];
  createdAt: Date;
}

export class LeagueWallet extends Entity<LeagueWalletId> {
  readonly leagueId: LeagueId;
  readonly balance: Money;
  readonly transactionIds: TransactionId[];
  readonly createdAt: Date;

  private constructor(props: LeagueWalletProps) {
    super(props.id);
    
    this.leagueId = props.leagueId;
    this.balance = props.balance;
    this.transactionIds = props.transactionIds;
    this.createdAt = props.createdAt;
  }

  static create(props: {
    id: string;
    leagueId: string;
    balance: Money;
    createdAt?: Date;
    transactionIds?: string[];
  }): LeagueWallet {
    this.validate(props);

    const id = LeagueWalletId.create(props.id);
    const leagueId = LeagueId.create(props.leagueId);
    const transactionIds = props.transactionIds?.map(tid => TransactionId.create(tid)) ?? [];

    return new LeagueWallet({
      id,
      leagueId,
      balance: props.balance,
      transactionIds,
      createdAt: props.createdAt ?? new Date(),
    });
  }

  static rehydrate(props: {
    id: string;
    leagueId: string;
    balance: Money;
    transactionIds: string[];
    createdAt: Date;
  }): LeagueWallet {
    const id = LeagueWalletId.create(props.id);
    const leagueId = LeagueId.create(props.leagueId);
    const transactionIds = props.transactionIds.map(tid => TransactionId.create(tid));

    return new LeagueWallet({
      id,
      leagueId,
      balance: props.balance,
      transactionIds,
      createdAt: props.createdAt,
    });
  }

  private static validate(props: {
    id: string;
    leagueId: string;
    balance: Money;
  }): void {
    if (!props.id || props.id.trim().length === 0) {
      throw new RacingDomainValidationError('LeagueWallet ID is required');
    }

    if (!props.leagueId || props.leagueId.trim().length === 0) {
      throw new RacingDomainValidationError('LeagueWallet leagueId is required');
    }

    if (!props.balance) {
      throw new RacingDomainValidationError('LeagueWallet balance is required');
    }
  }

  /**
   * Add funds to wallet (from sponsorship or membership payments)
   */
  addFunds(netAmount: Money, transactionId: string): LeagueWallet {
    if (this.balance.currency !== netAmount.currency) {
      throw new RacingDomainInvariantError('Cannot add funds with different currency');
    }

    const newBalance = this.balance.add(netAmount);
    const newTransactionId = TransactionId.create(transactionId);

    return new LeagueWallet({
      ...this,
      balance: newBalance,
      transactionIds: [...this.transactionIds, newTransactionId],
    });
  }

  /**
   * Withdraw funds from wallet
   * Domain rule: Cannot withdraw if insufficient balance
   */
  withdrawFunds(amount: Money, transactionId: string): LeagueWallet {
    if (this.balance.currency !== amount.currency) {
      throw new RacingDomainInvariantError('Cannot withdraw funds with different currency');
    }

    if (!this.balance.isGreaterThan(amount) && !this.balance.equals(amount)) {
      throw new RacingDomainInvariantError('Insufficient balance for withdrawal');
    }

    const newBalance = this.balance.subtract(amount);
    const newTransactionId = TransactionId.create(transactionId);

    return new LeagueWallet({
      ...this,
      balance: newBalance,
      transactionIds: [...this.transactionIds, newTransactionId],
    });
  }

  /**
   * Check if wallet can withdraw a specific amount
   */
  canWithdraw(amount: Money): boolean {
    if (this.balance.currency !== amount.currency) {
      return false;
    }
    return this.balance.isGreaterThan(amount) || this.balance.equals(amount);
  }

  /**
   * Get current balance
   */
  getBalance(): Money {
    return this.balance;
  }

  /**
   * Get all transaction IDs
   */
  getTransactionIds(): string[] {
    return this.transactionIds.map(tid => tid.toString());
  }
}