import { faker } from '@faker-js/faker';
import type { League } from '@core/racing/domain/entities/League';
import { LeagueWallet } from '@core/racing/domain/entities/league-wallet/LeagueWallet';
import { LeagueWalletId } from '@core/racing/domain/entities/league-wallet/LeagueWalletId';
import { Transaction } from '@core/racing/domain/entities/league-wallet/Transaction';
import { TransactionId } from '@core/racing/domain/entities/league-wallet/TransactionId';
import { Money } from '@core/racing/domain/value-objects/Money';
import { seedId } from './SeedIdHelper';

type LeagueWalletSeed = {
  wallets: LeagueWallet[];
  transactions: Transaction[];
};

export class RacingLeagueWalletFactory {
  constructor(
    private readonly baseDate: Date,
    private readonly persistence: 'postgres' | 'inmemory' = 'inmemory',
  ) {}

  create(leagues: League[]): LeagueWalletSeed {
    const wallets: LeagueWallet[] = [];
    const transactions: Transaction[] = [];

    for (const league of leagues) {
      const leagueId = league.id.toString();
      const walletId = seedId(`wallet-${leagueId}`, this.persistence);
      const createdAt = faker.date.past({ years: 2, refDate: this.baseDate });

      // Ensure coverage:
      // - some leagues have no wallet (wallet use-case returns WALLET_NOT_FOUND)
      // - some wallets have no transactions
      // - some wallets have varied transaction types + statuses
      const shouldHaveWallet = leagueId !== 'league-2' && leagueId !== 'league-7';

      if (!shouldHaveWallet) {
        continue;
      }

      const currency: 'USD' | 'EUR' | 'GBP' = leagueId === 'league-1' ? 'EUR' : 'USD';

      const baselineBalance =
        leagueId === 'league-5'
          ? Money.create(2880, 'USD')
          : Money.create(faker.number.int({ min: 0, max: 12_000 }), currency);

      let wallet = LeagueWallet.create({
        id: walletId,
        leagueId,
        balance: baselineBalance,
        createdAt,
      });

      const transactionCount =
        leagueId === 'league-3'
          ? 0
          : leagueId === 'league-5'
            ? 14
            : faker.number.int({ min: 2, max: 18 });

      for (let i = 0; i < transactionCount; i++) {
        const id = TransactionId.create(seedId(`tx-${leagueId}-${i + 1}`, this.persistence));
        const type = this.pickTransactionType(i, leagueId);
        const amount = this.pickAmount(type, currency, leagueId);

        let status = this.pickStatus(type, i, leagueId);

        const isDebit = type === 'withdrawal' || type === 'refund';
        if (status === 'completed' && isDebit) {
          const expectedNetAmount = amount.calculateNetAmount();
          if (!wallet.canWithdraw(expectedNetAmount)) {
            status = type === 'withdrawal' ? 'failed' : 'cancelled';
          }
        }

        const tx = Transaction.create({
          id,
          walletId: LeagueWalletId.create(walletId),
          type,
          amount,
          status,
          completedAt: status === 'completed' ? faker.date.recent({ days: 180, refDate: this.baseDate }) : undefined,
          createdAt: faker.date.recent({ days: 240, refDate: this.baseDate }),
          description: this.describe(type, leagueId),
          metadata: {
            leagueId,
            source: type === 'sponsorship_payment' ? 'sponsorship' : 'wallet',
          },
        });

        transactions.push(tx);

        // Update wallet balance for completed transactions only.
        // Use netAmount because wallet stores net proceeds after platform fee.
        if (tx.status === 'completed') {
          if (tx.type === 'withdrawal' || tx.type === 'refund') {
            wallet = wallet.withdrawFunds(tx.netAmount, tx.id.toString());
          } else {
            wallet = wallet.addFunds(tx.netAmount, tx.id.toString());
          }
        }
      }

      // Explicit edge-case: pending prize payout shows up in "pending payouts"
      if (leagueId === 'league-5') {
        const pendingPrize = Transaction.create({
          id: TransactionId.create(seedId(`tx-${leagueId}-pending-prize`, this.persistence)),
          walletId: LeagueWalletId.create(walletId),
          type: 'prize_payout',
          amount: Money.create(600, currency),
          status: 'pending',
          createdAt: faker.date.recent({ days: 15, refDate: this.baseDate }),
          completedAt: undefined,
          description: 'Season prize pool payout (pending)',
          metadata: { seasonId: seedId('season-2', this.persistence), placement: 'P1-P3' },
        });

        transactions.push(pendingPrize);
      }

      wallets.push(wallet);
    }

    return { wallets, transactions };
  }

  private pickTransactionType(index: number, leagueId: string) {
    if (leagueId === 'league-5') {
      const types = [
        'sponsorship_payment',
        'membership_payment',
        'membership_payment',
        'sponsorship_payment',
        'prize_payout',
        'withdrawal',
      ] as const;
      return types[index % types.length]!;
    }

    const types = [
      'membership_payment',
      'sponsorship_payment',
      'membership_payment',
      'withdrawal',
      'refund',
      'prize_payout',
    ] as const;

    return types[index % types.length]!;
  }

  private pickAmount(type: Transaction['type'], currency: 'USD' | 'EUR' | 'GBP', leagueId: string): Money {
    const asUsd = (amount: number) => Money.create(amount, currency);

    switch (type) {
      case 'sponsorship_payment':
        return asUsd(leagueId === 'league-5' ? faker.number.int({ min: 400, max: 1600 }) : faker.number.int({ min: 150, max: 2400 }));
      case 'membership_payment':
        return asUsd(faker.number.int({ min: 10, max: 120 }));
      case 'withdrawal':
        return asUsd(faker.number.int({ min: 25, max: 800 }));
      case 'refund':
        return asUsd(faker.number.int({ min: 10, max: 150 }));
      case 'prize_payout':
        return asUsd(faker.number.int({ min: 100, max: 1500 }));
    }
  }

  private pickStatus(type: Transaction['type'], index: number, leagueId: string): Transaction['status'] {
    if (leagueId === 'league-5') {
      if (type === 'withdrawal' && index % 2 === 0) return 'completed';
      if (type === 'prize_payout' && index % 3 === 0) return 'pending';
      return 'completed';
    }

    if (type === 'withdrawal') {
      return faker.helpers.arrayElement(['completed', 'pending', 'failed'] as const);
    }

    if (type === 'refund') {
      return faker.helpers.arrayElement(['completed', 'cancelled'] as const);
    }

    return faker.helpers.arrayElement(['completed', 'pending'] as const);
  }

  private describe(type: Transaction['type'], leagueId: string): string {
    switch (type) {
      case 'sponsorship_payment':
        return leagueId === 'league-5' ? 'Season sponsorship payment' : 'Sponsorship payment';
      case 'membership_payment':
        return 'Membership fee';
      case 'withdrawal':
        return 'Owner withdrawal';
      case 'refund':
        return 'Refund processed';
      case 'prize_payout':
        return 'Prize payout';
    }
  }
}