import { Transaction } from '../../../domain/entities/transaction';
import { ITransactionRepository, TransactionFilters } from '../../../domain/repositories/transaction-repository';
import { IAddressRepository } from '../../../domain/repositories/address-repository';
import { GetTransactionsInput } from '../../dto/transaction.dto';

const collectAddressIds = (transactions: Transaction[]): string[] => {
  const ids = new Set<string>();
  for (const tx of transactions) {
    if (tx.from) ids.add(tx.from);
    if (tx.to) ids.add(tx.to);
    for (const path of tx.data) {
      if ('from' in path && path.from) ids.add(path.from);
      if ('to' in path && path.to) ids.add(path.to);
    }
  }
  return Array.from(ids);
};

export class GetTransactionsUseCase {
  constructor(
    private readonly transactionRepository: ITransactionRepository,
    private readonly addressRepository: IAddressRepository,
  ) {}

  async execute(input: GetTransactionsInput): Promise<Transaction[]> {
    const filters: TransactionFilters = {
      walletId: input.walletId,
      networkId: input.networkId,
      tokenId: input.tokenId,
      status: input.status,
      method: input.method,
    };

    const transactions = await this.transactionRepository.findByFilters(filters, {
      limit: input.limit,
      offset: input.offset,
    });

    const addressIds = collectAddressIds(transactions);
    const addresses = await this.addressRepository.findByIds(addressIds);
    const addressMap = new Map(addresses.map(a => [a.id, a.address]));

    return transactions.map(tx => {
      const dataWithAddresses = tx.data.map(path => ({
        ...path,
        fromAddress:
          'from' in path && path.from
            ? addressMap.get(path.from) ?? null
            : null,
        toAddress:
          'to' in path && path.to ? addressMap.get(path.to) ?? null : null,
      })) as Transaction['data'];

      return new Transaction(
        tx.id,
        tx.hash,
        tx.status,
        tx.block,
        tx.networkId,
        tx.timestamp,
        tx.from,
        tx.to,
        tx.interactedWith,
        tx.value,
        tx.fee,
        tx.gasPrice,
        tx.gasLimit,
        tx.method,
        dataWithAddresses,
        tx.createdAt,
        tx.updatedAt,
        tx.from ? addressMap.get(tx.from) ?? null : null,
        tx.to ? addressMap.get(tx.to) ?? null : null,
      );
    });
  }
}
