import {
  AssetClass,
  adaAssetClass,
} from '@3rd-eye-labs/cardano-offchain-common';
import { array as A, function as F, ord as Ord, string as Str } from 'fp-ts';
import {
  DerivedPythPriceSP,
  getAssetClassComparisonStr,
  InitialAssetParam,
  InitialCollateralAssetParam,
  InitialStablepoolParam,
  PythFeedParamsSP,
} from '../../src';
import { toHex } from '@lucid-evolution/lucid';
import { Rational, rationalFromInt } from '../../src/types/rational';

export const DEFAULT_INTEREST = 1_000_000n;
export const DEFAULT_PRICE = rationalFromInt(1n);

type IndigoOracleNftConfig = {
  tag: '_indigo_oracle_nft';
  tokenName: string;
  startPrice: Rational;
  params: {
    biasTime: bigint;
    expirationPeriod: bigint;
  };
};

type PythOracleConfig = {
  tag: '_pyth_oracle_nft';
  pythFeedParams: PythFeedParamsSP;
};

export type InitialCollateralAsset = {
  collateralAsset: AssetClass;
  extraDecimals: bigint;
  priceOracle: IndigoOracleNftConfig | PythOracleConfig;
  interestOracle: {
    tokenName: string;
    initialInterestRate: bigint;
    params: {
      biasTime: bigint;
    };
  };
  redemptionRatio: Rational;
  maintenanceRatio: Rational;
  liquidationRatio: Rational;
  minCollateralAmt: bigint;
  firstCollateralAsset: boolean;
  nextCollateralAsset: AssetClass | undefined;
};

export type InitialAsset = {
  name: string;
  collateralAssets: InitialCollateralAsset[];
  debtMintingFeeRatio: Rational;
  liquidationProcessingFeeRatio: Rational;
  stabilityPoolWithdrawalFeeRatio: Rational;
  redemptionReimbursementRatio: Rational;
  redemptionProcessingFeeRatio: Rational;
  firstAsset: boolean;
  nextAsset: string | undefined;
};

export function mkAssetsChain(
  assets: InitialAssetParam[],
): InitialAssetParam[] {
  const sortedAssets = F.pipe(
    assets,
    A.sort(Ord.contramap<string, InitialAssetParam>((a) => a.name)(Str.Ord)),
  );

  return sortedAssets.map((asset, idx) => {
    const isFirst = idx === 0;
    const hasNext = idx < sortedAssets.length - 1;

    return {
      ...asset,
      firstAsset: isFirst,
      nextAsset: hasNext ? sortedAssets[idx + 1].name : undefined,
    } satisfies InitialAssetParam;
  });
}

export function mkCollateralAssetsChain(
  assets: InitialCollateralAssetParam[],
): InitialCollateralAssetParam[] {
  const sortedAssets = F.pipe(
    assets,
    A.sort(
      Ord.contramap<string, InitialCollateralAssetParam>((a) =>
        getAssetClassComparisonStr(a.collateralAsset),
      )(Str.Ord),
    ),
  );

  return sortedAssets.map((asset, idx) => {
    const isFirst = idx === 0;
    const hasNext = idx < sortedAssets.length - 1;

    return {
      ...asset,
      firstCollateralAsset: isFirst,
      nextCollateralAsset: hasNext
        ? sortedAssets[idx + 1].collateralAsset
        : undefined,
    } satisfies InitialCollateralAssetParam;
  });
}

export const mkBaseCollateralAsset = (
  a: AssetClass,
  interest: bigint = DEFAULT_INTEREST,
  initialPrice: Rational = DEFAULT_PRICE,
  extraDecimals: bigint = 0n,
  pythStateNft?: AssetClass,
  pythDerivedPrice?: DerivedPythPriceSP,
): InitialCollateralAssetParam => ({
  collateralAsset: a,
  extraDecimals: extraDecimals,
  priceOracle:
    pythStateNft && pythDerivedPrice
      ? {
          tag: '_pyth_oracle_nft',
          pythFeedParams: {
            config: pythDerivedPrice,
            pythStatePolicyId: {
              unCurrencySymbol: toHex(pythStateNft.currencySymbol),
            },
          },
        }
      : {
          tag: '_indigo_oracle_nft',
          tokenName: 'IASSET_ADA_ORACLE',
          startPrice: initialPrice,
          params: {
            biasTime: 120_000n,
            expirationPeriod: 1_800_000n,
          },
        },
  interestOracle: {
    tokenName: 'IASSET_ADA_INTEREST_ORACLE',
    initialInterestRate: interest,
    params: {
      biasTime: 120_000n,
    },
  },
  redemptionRatio: rationalFromInt(2n),
  maintenanceRatio: { numerator: 15n, denominator: 10n },
  liquidationRatio: { numerator: 12n, denominator: 10n },
  minCollateralAmt: 10_000_000n,
  firstCollateralAsset: true,
  nextCollateralAsset: undefined,
});

export const iusdInitialAssetCfg = (
  interest: bigint = DEFAULT_INTEREST,
  stablepools: InitialStablepoolParam[] = [],
): InitialAssetParam => ({
  name: 'iUSD',
  collateralAssets: [mkBaseCollateralAsset(adaAssetClass, interest)],
  debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
  liquidationProcessingFeeRatio: { numerator: 2n, denominator: 100n },
  stabilityPoolWithdrawalFeeRatio: { numerator: 5n, denominator: 1_000n },
  redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
  redemptionProcessingFeeRatio: { numerator: 1n, denominator: 100n },
  firstAsset: true,
  nextAsset: undefined,
  stablepools,
});

// Creates iUSD with a Pyth price oracle
export const iusdInitialAssetCfgWithPyth = (
  pythStatePolicyId: AssetClass,
  mkCollaterals: (
    pythStatePolicyId: AssetClass,
  ) => InitialCollateralAssetParam[],
): InitialAssetParam => ({
  name: 'iUSD',
  collateralAssets: mkCollaterals(pythStatePolicyId),
  stablepools: [],
  debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
  liquidationProcessingFeeRatio: { numerator: 2n, denominator: 100n },
  stabilityPoolWithdrawalFeeRatio: { numerator: 5n, denominator: 1_000n },
  redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
  redemptionProcessingFeeRatio: { numerator: 1n, denominator: 100n },
  firstAsset: true,
  nextAsset: undefined,
});

// Creates iETH with a Pyth price oracle
export const iethInitialAssetCfgWithPyth = (
  pythStatePolicyId: AssetClass,
  mkCollaterals: (
    pythStatePolicyId: AssetClass,
  ) => InitialCollateralAssetParam[],
): InitialAssetParam => ({
  name: 'iETH',
  collateralAssets: mkCollaterals(pythStatePolicyId),
  stablepools: [],
  debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
  liquidationProcessingFeeRatio: { numerator: 2n, denominator: 100n },
  stabilityPoolWithdrawalFeeRatio: { numerator: 5n, denominator: 1_000n },
  redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
  redemptionProcessingFeeRatio: { numerator: 1n, denominator: 100n },
  firstAsset: true,
  nextAsset: undefined,
});

// Creates iBTC with a Pyth price oracle
export const ibtcInitialAssetCfgWithPyth = (
  pythStatePolicyId: AssetClass,
  mkCollaterals: (
    pythStatePolicyId: AssetClass,
  ) => InitialCollateralAssetParam[],
): InitialAssetParam => ({
  name: 'iBTC',
  collateralAssets: mkCollaterals(pythStatePolicyId),
  stablepools: [],
  debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
  liquidationProcessingFeeRatio: { numerator: 2n, denominator: 100n },
  stabilityPoolWithdrawalFeeRatio: { numerator: 5n, denominator: 1_000n },
  redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
  redemptionProcessingFeeRatio: { numerator: 1n, denominator: 100n },
  firstAsset: true,
  nextAsset: undefined,
});

export const ieurInitialAssetCfg = (
  interest: bigint = DEFAULT_INTEREST,
): InitialAssetParam => ({
  name: 'iEUR',
  collateralAssets: [mkBaseCollateralAsset(adaAssetClass, interest)],
  stablepools: [],
  debtMintingFeeRatio: { numerator: 5n, denominator: 1_000n },
  liquidationProcessingFeeRatio: { numerator: 2n, denominator: 100n },
  stabilityPoolWithdrawalFeeRatio: { numerator: 5n, denominator: 1_000n },
  redemptionReimbursementRatio: { numerator: 1n, denominator: 100n },
  redemptionProcessingFeeRatio: { numerator: 1n, denominator: 100n },
  firstAsset: true,
  nextAsset: undefined,
});
