import { TSchema, Data, Schema } from '@evolution-sdk/evolution';
import { DEFAULT_SCHEMA_OPTIONS } from '../../types/evolution-schema-options';
import { RationalSchema } from '../../types/rational';

const PythConfigurationSchema = TSchema.Struct({
  priceFeedId: TSchema.Integer,
});

export type PythConfiguration = typeof PythConfigurationSchema.Type;

const OpaqueData = Schema.typeSchema(Data.DataSchema);

export const DerivedPythPriceSchema = TSchema.Union(
  TSchema.Struct(
    {
      Value: TSchema.Struct(
        { configuration: PythConfigurationSchema },
        { flatFields: true },
      ),
    },
    { flatInUnion: true },
  ),
  TSchema.Struct(
    {
      Inverse: TSchema.Struct({ value: OpaqueData }, { flatFields: true }),
    },
    { flatInUnion: true },
  ),
  TSchema.Struct(
    {
      Divide: TSchema.Struct(
        { x: OpaqueData, y: OpaqueData },
        { flatFields: true },
      ),
    },
    { flatInUnion: true },
  ),
);

export type DerivedPythPrice = typeof DerivedPythPriceSchema.Type;

export function fromDataDerivedPythPrice(data: Data.Data): DerivedPythPrice {
  return Data.withSchema(DerivedPythPriceSchema).fromCBORHex(
    Data.toCBORHex(data),
  );
}

export function toDataDerivedPythPrice(
  derivedPythPrice: DerivedPythPrice,
): Data.Data {
  return Data.withSchema(DerivedPythPriceSchema).toData(derivedPythPrice);
}

const PythFeedParamsSchema = TSchema.Struct({
  config: DerivedPythPriceSchema,
  pythStatePolicyId: TSchema.ByteArray,
});

export type PythFeedParams = typeof PythFeedParamsSchema.Type;

export function serialisePythFeedParams(params: PythFeedParams): Data.Data {
  return Data.withSchema(PythFeedParamsSchema).toData(params);
}

const PythFeedRedeemerSchema = TSchema.Struct(
  {
    price: RationalSchema,
    auxiliaryData: OpaqueData,
  },
  { flatInUnion: true },
);
export type PythFeedRedeemer = typeof PythFeedRedeemerSchema.Type;

export function serialisePythFeedRedeemer(r: PythFeedRedeemer): string {
  return Data.withSchema(
    PythFeedRedeemerSchema,
    DEFAULT_SCHEMA_OPTIONS,
  ).toCBORHex(r);
}

/**
 * IntervalBoundType
 * A type of interval bound. The value for the `Finite` case typically
 represents a number of seconds or milliseconds.
 */
export const AikenIntervalIntervalBoundType = TSchema.Union(
  TSchema.TaggedStruct('NegativeInfinity', {}, { flatInUnion: true }),
  TSchema.TaggedStruct(
    'Finite',
    { finite: TSchema.Integer },
    { flatInUnion: true },
  ),
  TSchema.TaggedStruct('PositiveInfinity', {}, { flatInUnion: true }),
);

/**
 * IntervalBound
 * An interval bound, either inclusive or exclusive.
 */
export const AikenIntervalIntervalBound = TSchema.Struct({
  bound_type: AikenIntervalIntervalBoundType,
  is_inclusive: TSchema.Boolean,
});

/**
 * ValidityRange
 * A type to represent intervals of values. Interval are inhabited by an
 integer value representing  have a finite lower-bound and/or upper-bound.
 This allows to represent all kind of mathematical intervals:
 ```aiken
 // [1; 10]
 let i0: Interval = Interval
   { lower_bound:
       IntervalBound { bound_type: Finite(1), is_inclusive: True }
   , upper_bound:
       IntervalBound { bound_type: Finite(10), is_inclusive: True }
   }
 ```
 ```aiken
 // (20; infinity)
 let i1: Interval = Interval
   { lower_bound:
       IntervalBound { bound_type: Finite(20), is_inclusive: False }
   , upper_bound:
       IntervalBound { bound_type: PositiveInfinity, is_inclusive: False }
   }
 ```
 */
export const CardanoTransactionValidityRange = TSchema.Struct({
  lower_bound: AikenIntervalIntervalBound,
  upper_bound: AikenIntervalIntervalBound,
});

const PythStateDatumSchema = TSchema.Struct(
  {
    governance: TSchema.Struct(
      {
        wormhole: TSchema.ByteArray,
        emitterChain: TSchema.Integer,
        emitterAddress: TSchema.ByteArray,
        seenSequence: TSchema.Integer,
      },
      { flatInUnion: true },
    ),
    trustedSigners: TSchema.Map(
      TSchema.ByteArray,
      CardanoTransactionValidityRange,
    ),
    deprecatedWithdrawScripts: TSchema.Map(
      CardanoTransactionValidityRange,
      TSchema.ByteArray,
    ),
    withdraw_script: TSchema.ByteArray,
  },
  { flatInUnion: true },
);
export type PythStateDatum = typeof PythStateDatumSchema.Type;

export function serialisePythStateDatum(r: PythStateDatum): string {
  return Data.withSchema(
    PythStateDatumSchema,
    DEFAULT_SCHEMA_OPTIONS,
  ).toCBORHex(r);
}

export function parsePythStateDatum(datum: string): PythStateDatum {
  return Data.withSchema(
    PythStateDatumSchema,
    DEFAULT_SCHEMA_OPTIONS,
  ).fromCBORHex(datum);
}

const PythUpdatesRedeemerSchema = TSchema.Array(TSchema.ByteArray);
export type PythUpdatesRedeemer = typeof PythUpdatesRedeemerSchema.Type;

export function serialisePythUpdatesRedeemer(r: PythUpdatesRedeemer): string {
  return Data.withSchema(
    PythUpdatesRedeemerSchema,
    DEFAULT_SCHEMA_OPTIONS,
  ).toCBORHex(r);
}
