import { LucidEvolution, ScriptHash, UTxO } from '@lucid-evolution/lucid';

import { matchSingle, createScriptAddress } from '../../src';
import { option as O, array as A, function as F } from 'fp-ts';
import {
  AssetClass,
  assetClassToUnit,
  getRandomElement,
} from '@3rd-eye-labs/cardano-offchain-common';
import {
  parsePollManager,
  parsePollShard,
  PollManagerContent,
  PollShardContent,
} from '../../src/contracts/poll/types-poll-new';

export async function findPollManager(
  lucid: LucidEvolution,
  pollManagerScriptHash: ScriptHash,
  pollManagerNft: AssetClass,
  pollId: bigint,
): Promise<{ utxo: UTxO; datum: PollManagerContent }> {
  const pollManagerUtxos = await lucid.utxosAtWithUnit(
    createScriptAddress(lucid.config().network!, pollManagerScriptHash),
    assetClassToUnit(pollManagerNft),
  );

  return matchSingle(
    F.pipe(
      pollManagerUtxos.map((utxo) =>
        F.pipe(
          O.fromNullable(utxo.datum),
          O.flatMap(parsePollManager),
          O.flatMap((datum) => {
            if (datum.pollId === pollId) {
              return O.some({ utxo, datum: datum });
            } else {
              return O.none;
            }
          }),
        ),
      ),
      A.compact,
    ),
    (res) => new Error('Expected a single Gov UTXO.: ' + JSON.stringify(res)),
  );
}

export async function findAllPollShards(
  lucid: LucidEvolution,
  pollShardScriptHash: string,
  pollShardNft: AssetClass,
  pollId: bigint,
): Promise<{ utxo: UTxO; datum: PollShardContent }[]> {
  const pollShardUtxos = await lucid.utxosAtWithUnit(
    createScriptAddress(lucid.config().network!, pollShardScriptHash),
    assetClassToUnit(pollShardNft),
  );

  return F.pipe(
    pollShardUtxos.map((utxo) =>
      F.pipe(
        O.fromNullable(utxo.datum),
        O.flatMap(parsePollShard),
        O.flatMap((datum) => {
          if (datum.pollId === pollId) {
            return O.some({ utxo, datum: datum });
          } else {
            return O.none;
          }
        }),
      ),
    ),
    A.compact,
  );
}

export async function findRandomPollShard(
  lucid: LucidEvolution,
  pollShardScriptHash: string,
  pollShardNft: AssetClass,
  pollId: bigint,
): Promise<{ utxo: UTxO; datum: PollShardContent }> {
  const pollShardOuts = await findAllPollShards(
    lucid,
    pollShardScriptHash,
    pollShardNft,
    pollId,
  );

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