import { LucidEvolution, UTxO } from '@lucid-evolution/lucid';
import { fromSystemParamsAsset, SystemParams } from '../../src';
import { option as O, function as F } from 'fp-ts';
import {
  AssetClass,
  assetClassToUnit,
  assetClassValueOf,
  getRandomElement,
} from '@3rd-eye-labs/cardano-offchain-common';

export async function findRandomTreasuryUtxo(
  lucid: LucidEvolution,
  sysParams: SystemParams,
): Promise<UTxO> {
  // We need to consider both with staking credential and without.
  const treasuryUtxos = await lucid.utxosAt({
    hash: sysParams.validatorHashes.treasuryHash,
    type: 'Script',
  });

  return F.pipe(
    O.fromNullable(getRandomElement(treasuryUtxos)),
    O.match(() => {
      throw new Error('Expected some treasury UTXOs.');
    }, F.identity),
  );
}

export async function findRandomTreasuryUtxoWithOnlyAda(
  lucid: LucidEvolution,
  sysParams: SystemParams,
): Promise<UTxO> {
  // We need to consider both with staking credential and without.
  const treasuryUtxos = await lucid.utxosAt({
    hash: sysParams.validatorHashes.treasuryHash,
    type: 'Script',
  });

  const adaOnlyTreasuryUtxos = treasuryUtxos.filter(
    (utxo) => Object.keys(utxo.assets).length == 1,
  );

  return F.pipe(
    O.fromNullable(getRandomElement(adaOnlyTreasuryUtxos)),
    O.match(() => {
      throw new Error('Expected some treasury UTXOs.');
    }, F.identity),
  );
}

export async function findRandomTreasuryUtxoWithAsset(
  lucid: LucidEvolution,
  sysParams: SystemParams,
  asset: AssetClass,
  max_number_of_assets: bigint = 2n,
): Promise<UTxO> {
  // We need to consider both with staking credential and without.
  const treasuryUtxos = await lucid.utxosAtWithUnit(
    { hash: sysParams.validatorHashes.treasuryHash, type: 'Script' },
    assetClassToUnit(asset),
  );

  const validTreasuryUtxos = treasuryUtxos.filter(
    (utxo) => Object.keys(utxo.assets).length <= max_number_of_assets,
  );

  return F.pipe(
    O.fromNullable(getRandomElement(validTreasuryUtxos)),
    O.match(() => {
      throw new Error('Expected some treasury UTXOs.');
    }, F.identity),
  );
}

export async function findAllTreasuryUtxos(
  lucid: LucidEvolution,
  sysParams: SystemParams,
): Promise<UTxO[]> {
  // We need to consider both with staking credential and without.
  return lucid.utxosAt({
    type: 'Script',
    hash: sysParams.validatorHashes.treasuryHash,
  });
}

export async function findAllTreasuryUtxosWithNonAdaAsset(
  lucid: LucidEvolution,
  sysParams: SystemParams,
  includeDaoToken: boolean = true,
): Promise<UTxO[]> {
  // We need to consider both with staking credential and without.
  const treasuryUtxos = await lucid.utxosAt({
    hash: sysParams.validatorHashes.treasuryHash,
    type: 'Script',
  });

  const treasuryUtxosWithNonAdaAsset = treasuryUtxos.filter(
    (utxo) =>
      Object.keys(utxo.assets).length !== 1 &&
      (includeDaoToken
        ? true
        : assetClassValueOf(
            utxo.assets,
            fromSystemParamsAsset(sysParams.govParams.daoIdentityToken),
          ) === 0n),
  );

  return treasuryUtxosWithNonAdaAsset;
}
