import {
  addAssets,
  credentialToRewardAddress,
  fromHex,
  fromText,
  LucidEvolution,
  mintingPolicyToId,
  scriptHashToCredential,
  toHex,
  validatorToScriptHash,
} from '@lucid-evolution/lucid';
import {
  AssetClass,
  mkAssetsOf,
  mkLovelacesOf,
} from '@3rd-eye-labs/cardano-offchain-common';
import type { AssetInfo, InitialAssetParam, InitializeOptions } from './types';
import type {
  CdpParamsSP,
  CdpRedeemParamsSP,
  ExecuteParamsSP,
  PythConfig,
  PythFeedConfig,
  ScriptCredential,
  SystemParams,
  VersionRecordParams,
} from '../../types/system-params';
import type { CollectorParamsSP, GovParamsSP } from '../../types/system-params';
import type {
  CDPCreatorParamsSP,
  IAssetParamsSP,
  InterestCollectionParamsSP,
  PollManagerParamsSP,
  PollShardParamsSP,
  RobParamsSP,
  StabilityPoolParamsSP,
  StakingParamsSP,
  StableswapParamsSP,
  TreasuryParamsSP,
} from '../../types/system-params';
import type { ProtocolParams } from '../gov/types-new';
import {
  fromSysParamsPythFeedParams,
  toSystemParamsAsset,
} from '../../types/system-params';
import { mkAuthTokenPolicy } from '../../scripts/auth-token-policy';
import { mkIAssetTokenPolicy } from '../../scripts/iasset-policy';
import {
  mkVersionRecordTokenPolicy,
  mkVersionRegistryValidator,
} from '../version-registry/scripts';
import {
  mkCdpRedeemValidatorFromSP,
  mkCdpValidatorFromSP,
} from '../cdp/scripts';
import { mkExecuteValidatorFromSP } from '../execute/scripts';
import {
  mkPollManagerValidatorFromSP,
  mkPollShardValidatorFromSP,
} from '../poll/scripts';
import { mkRobValidatorFromSP } from '../rob/scripts';
import { mkStableswapValidatorFromSP } from '../stableswap/scripts';
import { mkCollectorValidatorFromSP } from '../collector/scripts';
import { mkInterestCollectionValidatorFromSP } from '../interest-collection/scripts';
import { mkIAssetValidatorFromSP } from '../iasset/scripts';
import { mkPythFeedValidator } from '../pyth-feed/scripts';
import { mkStakingValidatorFromSP } from '../staking/scripts';
import { mkStabilityPoolValidatorFromSP } from '../stability-pool/scripts';
import { mkTreasuryValidatorFromSP } from '../treasury/scripts';
import { mkCDPCreatorValidatorFromSP } from '../cdp-creator/scripts';
import { mkGovValidatorFromSP } from '../gov/scripts';
import { submitTx } from '../../utils/lucid-utils';
import {
  deriveAuthToken,
  initScriptRef,
  initCollector,
  initCDPCreator,
  initGovernance,
  initInterestCollector,
  initStakingManager,
  initTreasury,
  initializeAsset,
  mintAuthTokenDirect,
  mintOneTimeAsset,
  INIT_TOKEN_NAMES,
} from './helpers';
import { runAndAwaitTxBuilder } from '../../../tests/test-helpers';
import { match } from 'ts-pattern';
import { initPyth } from '../../../tests/pyth/endpoints';

const DEFAULT_STAKING_CRED: ScriptCredential = {
  tag: 'StakingHash',
  contents: {
    tag: 'ScriptCredential',
    contents: 'b8358aadd30c60eba168608ad5e875592e9b7cb8c700827cde87f9a3',
  },
};

/**
 * Initialize Pyth config from an existing Pyth state asset (e.g. from test's initPyth).
 * Caller is responsible for creating the Pyth state asset before calling this.
 */
export async function initPythConfig(
  lucid: LucidEvolution,
  initialAssets: InitialAssetParam[],
  pythStateAsset: AssetClass,
): Promise<PythConfig> {
  let allPythFeeds: { [key: string]: PythFeedConfig } = {};

  for (const asset of initialAssets) {
    for (const collateral of asset.collateralAssets) {
      const result = await match(collateral.priceOracle)
        .returnType<Promise<{ [key: string]: PythFeedConfig }>>()
        .with({ tag: '_pyth_oracle_nft' }, async (val) => {
          const pythFeedValidator = mkPythFeedValidator(
            fromSysParamsPythFeedParams(val.pythFeedParams),
          );
          const pythFeedValHash = validatorToScriptHash(pythFeedValidator);
          const pythFeedValScriptRef = await initScriptRef(
            lucid,
            pythFeedValidator,
          );

          // Register the stake key for pyth feed validator.
          await runAndAwaitTxBuilder(
            lucid,
            lucid.newTx().register.Stake(
              credentialToRewardAddress(lucid.config().network!, {
                hash: validatorToScriptHash(pythFeedValidator),
                type: 'Script',
              }),
            ),
          );

          const key = `${asset.name}/${toHex(collateral.collateralAsset.currencySymbol)}.${toHex(collateral.collateralAsset.tokenName)}`;

          return {
            [key]: {
              params: val.pythFeedParams,
              pythFeedValHash,
              pythFeedValScriptRef: { input: pythFeedValScriptRef },
            } satisfies PythFeedConfig,
          };
        })
        .otherwise(() => Promise.resolve({}));

      allPythFeeds = { ...allPythFeeds, ...result };
    }
  }

  return {
    pythStateAssetClass: toSystemParamsAsset(pythStateAsset),
    pythFeeds: allPythFeeds,
  };
}

export async function init(
  lucid: LucidEvolution,
  defaultInitialAssets: InitialAssetParam[],
  currentSlot: number,
  mkPythBasedAssets: (
    pythStateNft: AssetClass,
  ) => InitialAssetParam[] = () => [],
  protocolParams: ProtocolParams,
  initOptions: InitializeOptions,
  spStakingCred: ScriptCredential | undefined = DEFAULT_STAKING_CRED,
  treasuryStakingCred: ScriptCredential | undefined = DEFAULT_STAKING_CRED,
): Promise<[SystemParams, AssetInfo[]]> {
  const startSlot = lucid.currentSlot();

  const {
    totalIndySupply,
    numCdpCreators,
    treasuryIndyAmount,
    numCollectors,
    numInterestCollectors,
    numTreasuryUtxos,
    accountCreateFeeLovelaces,
    accountProcessingCooldownMs,
    interestSettlementCooldown,
    partialRedemptionExtraFeeLovelace,
    biasTime,
    gBiasTime,
    accountProcessingBiasTime,
    interestCollectorUtxoLovelaces,
  } = initOptions;
  const tn = INIT_TOKEN_NAMES;

  const indyAsset = await mintOneTimeAsset(lucid, tn.indy, totalIndySupply);
  const daoAsset = await mintOneTimeAsset(lucid, tn.dao, 1n);
  const govNftAsset = await mintOneTimeAsset(lucid, tn.govNft, 1n);

  const pollToken = deriveAuthToken(govNftAsset, tn.pollManager);
  const upgradeToken = deriveAuthToken(pollToken, tn.upgrade);
  const iassetToken = deriveAuthToken(upgradeToken, tn.iasset);
  const collateralAssetAuthToken = deriveAuthToken(
    iassetToken,
    tn.collateralAsset,
  );
  const stabilityPoolToken = deriveAuthToken(upgradeToken, tn.stabilityPool);

  const versionRecordParams: VersionRecordParams = {
    upgradeToken: toSystemParamsAsset(upgradeToken),
  };
  const versionRecordTokenPolicy = mkVersionRecordTokenPolicy({
    upgradeToken: {
      currencySymbol: toHex(upgradeToken.currencySymbol),
      tokenName: toHex(upgradeToken.tokenName),
    },
  });
  const versionRecordToken: AssetClass = {
    currencySymbol: fromHex(mintingPolicyToId(versionRecordTokenPolicy)),
    tokenName: fromHex(fromText(tn.versionRecord)),
  };

  const versionRegistryValidator = mkVersionRegistryValidator();
  const versionRegistryValHash = validatorToScriptHash(
    versionRegistryValidator,
  );

  const cdpCreatorAsset = await mintOneTimeAsset(
    lucid,
    tn.cdpCreator,
    numCdpCreators,
  );
  const cdpToken = deriveAuthToken(cdpCreatorAsset, tn.cdp);

  const stakingManagerAsset = await mintOneTimeAsset(
    lucid,
    tn.stakingManager,
    1n,
  );
  const stakingToken = deriveAuthToken(stakingManagerAsset, tn.staking);

  const collectorParams: CollectorParamsSP = {
    stakingManagerNFT: toSystemParamsAsset(stakingManagerAsset),
    stakingToken: toSystemParamsAsset(stakingToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
  };
  const collectorValidator = mkCollectorValidatorFromSP(collectorParams);
  const collectorValHash = validatorToScriptHash(collectorValidator);

  const stakingParams: StakingParamsSP = {
    stakingManagerNFT: toSystemParamsAsset(stakingManagerAsset),
    stakingToken: toSystemParamsAsset(stakingToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    pollToken: toSystemParamsAsset(pollToken),
    indyToken: toSystemParamsAsset(indyAsset),
    collectorValHash: collectorValHash,
  };
  const stakingValHash = validatorToScriptHash(
    mkStakingValidatorFromSP(stakingParams),
  );

  await initStakingManager(lucid, stakingParams);

  const iassetParams: IAssetParamsSP = {
    upgradeToken: toSystemParamsAsset(upgradeToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
  };
  const iassetValidator = mkIAssetValidatorFromSP(iassetParams);
  const iassetValHash = validatorToScriptHash(iassetValidator);

  const assetSymbolPolicy = mkIAssetTokenPolicy(cdpToken);
  const assetSymbol = mintingPolicyToId(assetSymbolPolicy);

  const snapshotEpochToScaleToSumToken = deriveAuthToken(
    stabilityPoolToken,
    tn.snapshotEpochToScaleToSum,
  );
  const accountToken = deriveAuthToken(stabilityPoolToken, tn.account);

  const stabilityPoolParams: StabilityPoolParamsSP = {
    assetSymbol: { unCurrencySymbol: assetSymbol },
    stabilityPoolToken: toSystemParamsAsset(stabilityPoolToken),
    snapshotEpochToScaleToSumToken: toSystemParamsAsset(
      snapshotEpochToScaleToSumToken,
    ),
    accountToken: toSystemParamsAsset(accountToken),
    cdpToken: toSystemParamsAsset(cdpToken),
    iAssetAuthToken: toSystemParamsAsset(iassetToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    iassetValHash: iassetValHash,
    accountCreateFeeLovelaces: Number(accountCreateFeeLovelaces),
    accountProcessingCooldownMs: Number(accountProcessingCooldownMs),
    accountProcessingBiasMs: accountProcessingBiasTime
      ? Number(accountProcessingBiasTime)
      : Number(biasTime),
    stakeCredential: spStakingCred,
  };

  const stabilityPoolValidator =
    mkStabilityPoolValidatorFromSP(stabilityPoolParams);
  const stabilityPoolValHash = validatorToScriptHash(stabilityPoolValidator);

  const treasuryParams: TreasuryParamsSP = {
    upgradeToken: toSystemParamsAsset(upgradeToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    treasuryUtxosStakeCredential: treasuryStakingCred,
  };

  const treasuryValidator = mkTreasuryValidatorFromSP(treasuryParams);
  const treasuryValHash = validatorToScriptHash(treasuryValidator);

  await initTreasury(
    lucid,
    treasuryParams,
    daoAsset,
    indyAsset,
    treasuryIndyAmount,
    numTreasuryUtxos,
  );

  const multisigUtxoNft = await mintOneTimeAsset(lucid, tn.multisigUtxo, 1n);

  const interestCollectionParams: InterestCollectionParamsSP = {
    versionRecordNft: toSystemParamsAsset(versionRecordToken),
    multisigUtxoNft: toSystemParamsAsset(multisigUtxoNft),
    cdpAuthTk: toSystemParamsAsset(cdpToken),
    collateralAssetAuthTk: toSystemParamsAsset(collateralAssetAuthToken),
    govAuthTk: toSystemParamsAsset(govNftAsset),
    cdpAssetSymbol: { unCurrencySymbol: assetSymbol },
    cdpBiasTime: BigInt(biasTime),
    interestSettlementCooldown,
  };

  const interestCollectionValidator = mkInterestCollectionValidatorFromSP(
    interestCollectionParams,
  );
  const interestCollectionValHash = validatorToScriptHash(
    interestCollectionValidator,
  );

  await initInterestCollector(
    lucid,
    interestCollectionValHash,
    multisigUtxoNft,
    numInterestCollectors,
    interestCollectorUtxoLovelaces,
  );
  const pythStateNft = await initPyth(lucid);

  const initialAssets = [
    ...defaultInitialAssets,
    ...mkPythBasedAssets(pythStateNft),
  ];

  const pythConfig = await initPythConfig(lucid, initialAssets, pythStateNft);

  const cdpRedeemParams: CdpRedeemParamsSP = {
    cdpAuthToken: toSystemParamsAsset(cdpToken),
    cdpAssetSymbol: { unCurrencySymbol: assetSymbol },
    iAssetAuthToken: toSystemParamsAsset(iassetToken),
    collateralAssetAuthToken: toSystemParamsAsset(collateralAssetAuthToken),
    interestCollectorValHash: interestCollectionValHash,
    iassetValHash: iassetValHash,
    treasuryValHash: treasuryValHash,
    govNFT: toSystemParamsAsset(govNftAsset),
    partialRedemptionExtraFeeLovelace,
    biasTime,
  };

  const cdpRedeemValidator = mkCdpRedeemValidatorFromSP(cdpRedeemParams);
  const cdpRedeemValHash = validatorToScriptHash(cdpRedeemValidator);

  const cdpRedeemRewardAddr = credentialToRewardAddress(
    lucid.config().network!,
    scriptHashToCredential(cdpRedeemValHash),
  );

  await submitTx(lucid, lucid.newTx().register.Stake(cdpRedeemRewardAddr));

  const cdpParams: CdpParamsSP = {
    cdpAuthToken: toSystemParamsAsset(cdpToken),
    cdpAssetSymbol: { unCurrencySymbol: assetSymbol },
    iAssetAuthToken: toSystemParamsAsset(iassetToken),
    collateralAssetAuthToken: toSystemParamsAsset(collateralAssetAuthToken),
    stabilityPoolAuthToken: toSystemParamsAsset(stabilityPoolToken),
    upgradeToken: toSystemParamsAsset(upgradeToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    interestCollectorValHash: interestCollectionValHash,
    spValHash: stabilityPoolValHash,
    iassetValHash: iassetValHash,
    treasuryValHash: treasuryValHash,
    cdpRedeemValHash: cdpRedeemValHash,
    biasTime,
  };
  const cdpValidator = mkCdpValidatorFromSP(cdpParams);
  const cdpValHash = validatorToScriptHash(cdpValidator);

  const cdpCreatorParams: CDPCreatorParamsSP = {
    cdpCreatorNft: toSystemParamsAsset(cdpCreatorAsset),
    cdpAssetCs: { unCurrencySymbol: assetSymbol },
    cdpAuthTk: toSystemParamsAsset(cdpToken),
    iAssetAuthTk: toSystemParamsAsset(iassetToken),
    collateralAssetAuthTk: toSystemParamsAsset(collateralAssetAuthToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    upgradeToken: toSystemParamsAsset(upgradeToken),
    iassetValHash: iassetValHash,
    cdpScriptHash: cdpValHash,
    treasuryValHash: treasuryValHash,
    biasTime: cdpParams.biasTime,
  };
  const cdpCreatorValidator = mkCDPCreatorValidatorFromSP(cdpCreatorParams);
  const cdpCreatorValHash = validatorToScriptHash(cdpCreatorValidator);

  const stableswapParams: StableswapParamsSP = {
    iassetSymbol: { unCurrencySymbol: assetSymbol },
    cdpToken: toSystemParamsAsset(cdpToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    cdpValHash: cdpValHash,
    treasuryValHash: treasuryValHash,
  };
  const stableswapValidator = mkStableswapValidatorFromSP(stableswapParams);
  const stableswapValHash = validatorToScriptHash(stableswapValidator);

  await initCollector(lucid, collectorParams, numCollectors);

  const assetInfos: AssetInfo[] = [];
  if (initialAssets.length > 0) {
    await mintAuthTokenDirect(lucid, govNftAsset, tn.pollManager, 1n);
    await mintAuthTokenDirect(lucid, pollToken, tn.upgrade, 1n);
    if (initialAssets.length > 0) {
      await mintAuthTokenDirect(
        lucid,
        upgradeToken,
        tn.iasset,
        BigInt(initialAssets.length),
      );
    }

    const totalCollateralAssetsCount = initialAssets.reduce(
      (acc, asset) => acc + asset.collateralAssets.length,
      0,
    );
    const totalStablePoolsCount = initialAssets.reduce(
      (acc, asset) => acc + asset.stablepools.length,
      0,
    );

    if (totalCollateralAssetsCount > 0n) {
      // We must first send a single cdp creator asset to the wallet address to avoid the "Must consume exactly 1 asset" error
      await runAndAwaitTxBuilder(
        lucid,
        lucid
          .newTx()
          .pay.ToAddress(
            await lucid.wallet().address(),
            addAssets(
              mkAssetsOf(iassetToken, 1n),
              mkLovelacesOf(BigInt(totalCollateralAssetsCount) * 5000000n),
            ),
          ),
      );

      await mintAuthTokenDirect(
        lucid,
        iassetToken,
        tn.collateralAsset,
        BigInt(totalCollateralAssetsCount),
      );
    }

    if (totalStablePoolsCount > 0n) {
      // We must first send a single cdp creator asset to the wallet address to avoid the "Must consume exactly 1 asset" error
      await runAndAwaitTxBuilder(
        lucid,
        lucid
          .newTx()
          .pay.ToAddress(
            await lucid.wallet().address(),
            addAssets(
              mkAssetsOf(cdpCreatorAsset, 1n),
              mkLovelacesOf(BigInt(totalStablePoolsCount) * 5000000n),
            ),
          ),
      );
      await mintAuthTokenDirect(
        lucid,
        cdpCreatorAsset,
        tn.cdp,
        BigInt(totalStablePoolsCount),
      );
    }

    if (initialAssets.length > 0) {
      await mintAuthTokenDirect(
        lucid,
        upgradeToken,
        tn.stabilityPool,
        BigInt(initialAssets.length),
      );

      for (const asset of initialAssets) {
        const assetInfo = await initializeAsset(
          lucid,
          iassetParams,
          iassetToken,
          collateralAssetAuthToken,
          cdpToken,
          cdpParams,
          stableswapValHash,
          stabilityPoolParams,
          stabilityPoolToken,
          asset,
          pythConfig,
          currentSlot,
        );

        assetInfos.push(assetInfo);
      }
    }

    await mintAuthTokenDirect(lucid, pollToken, tn.upgrade, -1n);
    await mintAuthTokenDirect(lucid, govNftAsset, tn.pollManager, -1n);
  }

  // Send the CDP creator UTxOs after initializing the assets to allow for the creation of stableswap pools
  await initCDPCreator(lucid, cdpCreatorParams, numCdpCreators);

  const executeParams: ExecuteParamsSP = {
    govNFT: toSystemParamsAsset(govNftAsset),
    upgradeToken: toSystemParamsAsset(upgradeToken),
    iAssetToken: toSystemParamsAsset(iassetToken),
    collateralAssetToken: toSystemParamsAsset(collateralAssetAuthToken),
    stabilityPoolToken: toSystemParamsAsset(stabilityPoolToken),
    cdpCreatorToken: toSystemParamsAsset(cdpCreatorAsset),
    cdpToken: toSystemParamsAsset(cdpToken),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    iassetValHash: iassetValHash,
    cdpValHash: cdpValHash,
    sPoolValHash: stabilityPoolValHash,
    versionRegistryValHash: versionRegistryValHash,
    treasuryValHash: treasuryValHash,
  };
  const executeValidator = mkExecuteValidatorFromSP(executeParams);
  const executeValHash = validatorToScriptHash(executeValidator);

  const pollShardParams: PollShardParamsSP = {
    pollToken: toSystemParamsAsset(pollToken),
    stakingToken: toSystemParamsAsset(stakingToken),
    indyAsset: toSystemParamsAsset(indyAsset),
    stakingValHash: stakingValHash,
  };
  const pollShardValidator = mkPollShardValidatorFromSP(pollShardParams);
  const pollShardValHash = validatorToScriptHash(pollShardValidator);

  const pollManagerParams: PollManagerParamsSP = {
    govNFT: toSystemParamsAsset(govNftAsset),
    pollToken: toSystemParamsAsset(pollToken),
    upgradeToken: toSystemParamsAsset(upgradeToken),
    indyAsset: toSystemParamsAsset(indyAsset),
    govExecuteValHash: executeValHash,
    pBiasTime: BigInt(biasTime),
    shardsValHash: pollShardValHash,
    treasuryValHash: treasuryValHash,
  };
  const pollManagerValidator = mkPollManagerValidatorFromSP(pollManagerParams);
  const pollManagerValHash = validatorToScriptHash(pollManagerValidator);

  const govParams: GovParamsSP = {
    gBiasTime: gBiasTime ? BigInt(gBiasTime) : BigInt(biasTime),
    govNFT: toSystemParamsAsset(govNftAsset),
    pollToken: toSystemParamsAsset(pollToken),
    upgradeToken: toSystemParamsAsset(upgradeToken),
    indyAsset: toSystemParamsAsset(indyAsset),
    versionRecordToken: toSystemParamsAsset(versionRecordToken),
    pollManagerValHash: pollManagerValHash,
    iassetValHash: iassetValHash,
    daoIdentityToken: toSystemParamsAsset(daoAsset),
    iAssetAuthToken: toSystemParamsAsset(iassetToken),
    iassetSymbol: { unCurrencySymbol: assetSymbol },
  };
  const govValidator = mkGovValidatorFromSP(govParams);
  const govValHash = validatorToScriptHash(govValidator);

  await initGovernance(
    lucid,
    govParams,
    govNftAsset,
    defaultInitialAssets,
    protocolParams,
  );

  const robParams: RobParamsSP = {
    iassetAuthToken: cdpParams.iAssetAuthToken,
    collateralAssetAuthToken: cdpParams.collateralAssetAuthToken,
    iassetPolicyId: cdpParams.cdpAssetSymbol,
    iassetValHash: iassetValHash,
    versionRecordToken: cdpParams.versionRecordToken,
  };

  const robValidator = mkRobValidatorFromSP(robParams);
  const robValHash = validatorToScriptHash(robValidator);

  // Policy scripts needed for script refs (deriveAuthToken only returns AssetClass)
  const cdpTokenPolicy = mkAuthTokenPolicy(cdpCreatorAsset, fromText(tn.cdp));
  const iassetTokenPolicy = mkAuthTokenPolicy(
    upgradeToken,
    fromText(tn.iasset),
  );
  const accountTokenPolicy = mkAuthTokenPolicy(
    stabilityPoolToken,
    fromText(tn.account),
  );
  const stabilityPoolTokenPolicy = mkAuthTokenPolicy(
    upgradeToken,
    fromText(tn.stabilityPool),
  );
  const pollTokenPolicy = mkAuthTokenPolicy(
    govNftAsset,
    fromText(tn.pollManager),
  );
  const stakingTokenPolicy = mkAuthTokenPolicy(
    stakingManagerAsset,
    fromText(tn.staking),
  );
  const collateralAssetAuthTokenPolicy = mkAuthTokenPolicy(
    iassetToken,
    fromText(tn.collateralAsset),
  );
  const upgradeTokenPolicy = mkAuthTokenPolicy(pollToken, fromText(tn.upgrade));
  const snapshotEpochToScaleToSumTokenPolicy = mkAuthTokenPolicy(
    stabilityPoolToken,
    fromText(tn.snapshotEpochToScaleToSum),
  );

  const sysParams: SystemParams = {
    cdpParams: cdpParams,
    iassetParams: iassetParams,
    cdpCreatorParams: cdpCreatorParams,
    cdpRedeemParams: cdpRedeemParams,
    collectorParams: collectorParams,
    executeParams: executeParams,
    govParams: govParams,
    stakingParams: stakingParams,
    stabilityPoolParams: stabilityPoolParams,
    stableswapParams: stableswapParams,
    treasuryParams: treasuryParams,
    pollShardParams: pollShardParams,
    pollManagerParams: pollManagerParams,
    interestCollectionParams: interestCollectionParams,
    indyToken: toSystemParamsAsset(indyAsset),
    robParams: robParams,
    versionRecordParams: versionRecordParams,
    startTime: {
      slot: startSlot,
      blockHeader: 'TODO',
    },
    scriptReferences: {
      interestCollectionValidatorRef: {
        input: await initScriptRef(lucid, interestCollectionValidator),
      },
      robValidatorRef: {
        input: await initScriptRef(lucid, robValidator),
      },
      cdpCreatorValidatorRef: {
        input: await initScriptRef(lucid, cdpCreatorValidator),
      },
      cdpValidatorRef: {
        input: await initScriptRef(lucid, cdpValidator),
      },
      cdpRedeemValidatorRef: {
        input: await initScriptRef(lucid, cdpRedeemValidator),
      },
      collectorValidatorRef: {
        input: await initScriptRef(lucid, collectorValidator),
      },
      executeValidatorRef: {
        input: await initScriptRef(lucid, executeValidator),
      },
      pollShardValidatorRef: {
        input: await initScriptRef(lucid, pollShardValidator),
      },
      pollManagerValidatorRef: {
        input: await initScriptRef(lucid, pollManagerValidator),
      },
      iAssetTokenPolicyRef: {
        input: await initScriptRef(lucid, assetSymbolPolicy),
      },
      stakingValidatorRef: {
        input: await initScriptRef(
          lucid,
          mkStakingValidatorFromSP(stakingParams),
        ),
      },
      stabilityPoolValidatorRef: {
        input: await initScriptRef(lucid, stabilityPoolValidator),
      },
      stableswapValidatorRef: {
        input: await initScriptRef(lucid, stableswapValidator),
      },
      treasuryValidatorRef: {
        input: await initScriptRef(lucid, treasuryValidator),
      },
      governanceValidatorRef: {
        input: await initScriptRef(lucid, govValidator),
      },
      versionRegistryValidatorRef: {
        input: await initScriptRef(lucid, versionRegistryValidator),
      },
      iassetValidatorRef: {
        input: await initScriptRef(lucid, iassetValidator),
      },
      authTokenPolicies: {
        cdpAuthTokenRef: {
          input: await initScriptRef(lucid, cdpTokenPolicy),
        },
        iAssetAuthTokenRef: {
          input: await initScriptRef(lucid, iassetTokenPolicy),
        },
        accountTokenRef: {
          input: await initScriptRef(lucid, accountTokenPolicy),
        },
        stabilityPoolAuthTokenRef: {
          input: await initScriptRef(lucid, stabilityPoolTokenPolicy),
        },
        pollManagerTokenRef: {
          input: await initScriptRef(lucid, pollTokenPolicy),
        },
        stakingTokenRef: {
          input: await initScriptRef(lucid, stakingTokenPolicy),
        },
        versionRecordTokenPolicyRef: {
          input: await initScriptRef(lucid, versionRecordTokenPolicy),
        },
        iAssetTokenRef: {
          input: await initScriptRef(lucid, assetSymbolPolicy),
        },
        collateralAssetTokenRef: {
          input: await initScriptRef(lucid, collateralAssetAuthTokenPolicy),
        },
        upgradeTokenRef: {
          input: await initScriptRef(lucid, upgradeTokenPolicy),
        },
        stabilityPoolTokenRef: {
          input: await initScriptRef(lucid, stabilityPoolTokenPolicy),
        },
        snapshotEpochToScaleToSumTokenRef: {
          input: await initScriptRef(
            lucid,
            snapshotEpochToScaleToSumTokenPolicy,
          ),
        },
      },
    },
    validatorHashes: {
      cdpCreatorHash: cdpCreatorValHash,
      cdpHash: cdpValHash,
      executeHash: executeValHash,
      govHash: govValHash,
      interestCollectionHash: interestCollectionValHash,
      pollShardHash: pollShardValHash,
      pollManagerHash: pollManagerValHash,
      treasuryHash: treasuryValHash,
      stabilityPoolHash: stabilityPoolValHash,
      stableswapHash: stableswapValHash,
      stakingHash: stakingValHash,
      collectorHash: collectorValHash,
      versionRegistryHash: versionRegistryValHash,
      robHash: robValHash,
      iassetHash: iassetValHash,
    },
    pythConfig,
  };

  return [sysParams, assetInfos];
}
