import { match, P } from 'ts-pattern';
import { Rational } from '../../types/rational';
import { decodePriceUpdate, decodePythMessage } from '../../utils/pyth';
import {
  DerivedPythPrice,
  fromDataDerivedPythPrice,
  PythConfiguration,
} from './types';
import { ParsedFeedPayload } from '@pythnetwork/pyth-lazer-sdk';
import { fromHex } from '@lucid-evolution/lucid';

/** Rational form { num, den } so we can do exact division (e.g. 1/0.00001 = 100000). */
export function derivePythPrice(
  derivedPythPrice: DerivedPythPrice,
  messageHex: string,
): Rational {
  const decodedMessage = decodePythMessage(fromHex(messageHex));
  const pricePayload = decodePriceUpdate(decodedMessage.payload);
  return match(derivedPythPrice)
    .with(
      { Value: P.select() },
      (value: { configuration: PythConfiguration }) => {
        const payload = pricePayload.priceFeeds.find(
          (p: ParsedFeedPayload) =>
            p.priceFeedId === Number(value.configuration.priceFeedId),
        );
        if (!payload)
          throw new Error(
            `Pyth payload not found for feed ID: ${value.configuration.priceFeedId}`,
          );
        const priceStr = payload.price;
        if (priceStr === undefined)
          throw new Error(`Pyth feed ${payload.priceFeedId} has no price`);
        const price = BigInt(priceStr);
        const exp = payload.exponent ?? 0;
        if (exp >= 0) {
          return { numerator: price * 10n ** BigInt(exp), denominator: 1n };
        }
        return { numerator: price, denominator: 10n ** BigInt(-exp) };
      },
    )
    .with({ Inverse: P.select() }, (inverse) => {
      const inner = derivePythPrice(
        fromDataDerivedPythPrice(inverse.value),
        messageHex,
      );
      return { numerator: inner.denominator, denominator: inner.numerator };
    })
    .with({ Divide: P.select() }, (divide) => {
      const x = derivePythPrice(fromDataDerivedPythPrice(divide.x), messageHex);
      const y = derivePythPrice(fromDataDerivedPythPrice(divide.y), messageHex);
      return {
        numerator: x.numerator * y.denominator,
        denominator: x.denominator * y.numerator,
      };
    })
    .exhaustive();
}
