import { match, P } from 'ts-pattern';
import { IAssetPriceInfo } from '../contracts/iasset/types';
import { Rational } from '../types/rational';
import { LucidEvolution, OutRef, UTxO } from '@lucid-evolution/lucid';
import { adjustPriceToDecimals } from '../contracts/cdp/helpers';
import {
  AssetClass,
  getInlineDatumOrThrow,
  matchSingle,
} from '@3rd-eye-labs/cardano-offchain-common';
import { parsePriceOracleDatum } from '../contracts/price-oracle/types-new';
import { derivePythPrice } from '../contracts/pyth-feed/helpers';
import {
  fromSysParamsDerivedPythPrice,
  getPythFeedConfig,
  PythConfig,
} from '../types/system-params';

export async function retrieveAdjustedPrice(
  iasset: Uint8Array<ArrayBufferLike>,
  collateralAsset: AssetClass,
  priceInfo: IAssetPriceInfo,
  extraDecimals: bigint,
  priceOracleOref: OutRef | undefined,
  pythMessage: string | undefined,
  pythConfig: PythConfig,
  lucid: LucidEvolution,
): Promise<[Rational, UTxO | undefined]> {
  const [oraclePrice, maybePriceOracleUtxo] = await match(priceInfo)
    .with({ OracleNft: P.any }, async () => {
      if (!priceOracleOref) throw new Error('Missing price oracle');
      const priceOracleUtxo = matchSingle(
        await lucid.utxosByOutRef([priceOracleOref]),
        (_) => new Error('Expected a single price oracle UTXO'),
      );
      const priceOracleDatum = parsePriceOracleDatum(
        getInlineDatumOrThrow(priceOracleUtxo),
      );
      return [priceOracleDatum.price, priceOracleUtxo];
    })
    .with({ Delisted: P.select() }, (price) => [price.price, undefined])
    .with({ DeferredValidation: P.select() }, () => {
      if (!pythMessage) throw new Error('Missing Pyth message');

      return [
        derivePythPrice(
          fromSysParamsDerivedPythPrice(
            getPythFeedConfig(pythConfig, iasset, collateralAsset).params
              .config,
          ),
          pythMessage,
        ),
        undefined,
      ];
    })
    .exhaustive();

  return [
    adjustPriceToDecimals(extraDecimals, oraclePrice as Rational),
    maybePriceOracleUtxo as UTxO | undefined,
  ];
}
