import { fromText, LucidEvolution, toHex, UTxO } from '@lucid-evolution/lucid';
import {
  fromSystemParamsAsset,
  matchSingle,
  mkStabilityPoolAddr,
  SystemParams,
} from '../../src';
import {
  AccountContent,
  parseAccountDatum,
  parseSnapshotEpochToScaleToSumDatum,
  parseStabilityPoolDatum,
  SnapshotEpochToScaleToSumContent,
  StabilityPoolContent,
} from '../../src/contracts/stability-pool/types-new';
import { assetClassToUnit } from '@3rd-eye-labs/cardano-offchain-common';
import { option as O, array as A, function as F } from 'fp-ts';

export async function findStabilityPool(
  lucid: LucidEvolution,
  sysParams: SystemParams,
  asset: string,
): Promise<{ utxo: UTxO; datum: StabilityPoolContent }> {
  // We need to consider both with staking credential and without.
  const spUtxos = await lucid.utxosAtWithUnit(
    { hash: sysParams.validatorHashes.stabilityPoolHash, type: 'Script' },
    assetClassToUnit(
      fromSystemParamsAsset(sysParams.stabilityPoolParams.stabilityPoolToken),
    ),
  );

  return matchSingle(
    F.pipe(
      spUtxos.map((utxo) =>
        F.pipe(
          O.fromNullable(utxo.datum),
          O.flatMap(parseStabilityPoolDatum),
          O.flatMap((datum) => {
            if (toHex(datum.iasset) == fromText(asset)) {
              return O.some({ utxo, datum: datum });
            } else {
              return O.none;
            }
          }),
        ),
      ),
      A.compact,
    ),
    (res) =>
      new Error(
        'Expected a single Stability Pool UTXO.: ' + JSON.stringify(res),
      ),
  );
}

export async function findStabilityPoolAccount(
  lucid: LucidEvolution,
  sysParams: SystemParams,
  owner: string,
  asset: string,
): Promise<{ utxo: UTxO; datum: AccountContent }> {
  // We need to consider both with staking credential and without.
  const spUtxos = await lucid.utxosAt({
    hash: sysParams.validatorHashes.stabilityPoolHash,
    type: 'Script',
  });

  return matchSingle(
    F.pipe(
      spUtxos.map((utxo) =>
        F.pipe(
          O.fromNullable(utxo.datum),
          O.flatMap(parseAccountDatum),
          O.flatMap((datum) => {
            if (
              toHex(datum.iasset) == fromText(asset) &&
              toHex(datum.owner) == owner
            ) {
              return O.some({ utxo, datum: datum });
            } else {
              return O.none;
            }
          }),
        ),
      ),
      A.compact,
    ),
    (res) =>
      new Error('Expected a single Account UTXO.: ' + JSON.stringify(res)),
  );
}

export async function findE2s2sSnapshots(
  lucid: LucidEvolution,
  sysParams: SystemParams,
  asset: string,
): Promise<{ utxo: UTxO; datum: SnapshotEpochToScaleToSumContent }[]> {
  // No need to query the ones without stake cred, e2s2s will always have stake cred based on script params.
  const spUtxos = await lucid.utxosAtWithUnit(
    mkStabilityPoolAddr(lucid, sysParams),
    assetClassToUnit(
      fromSystemParamsAsset(
        sysParams.stabilityPoolParams.snapshotEpochToScaleToSumToken,
      ),
    ),
  );

  return F.pipe(
    spUtxos.map((utxo) =>
      F.pipe(
        O.fromNullable(utxo.datum),
        O.flatMap(parseSnapshotEpochToScaleToSumDatum),
        O.flatMap((datum) => {
          if (toHex(datum.iasset) == fromText(asset)) {
            return O.some({ utxo, datum: datum });
          } else {
            return O.none;
          }
        }),
      ),
    ),
    A.compact,
  );
}
