import { Effect, Equal, FastCheck, Hash, Inspectable, ParseResult, Schema } from "effect";
import * as CBOR from "./CBOR.js";
import * as PlutusData from "./Data.js";
import * as Numeric from "./Numeric.js";
/**
 * Redeemer tag enum for different script execution contexts.
 *
 * CDDL: redeemer_tag = 0 ; spend | 1 ; mint | 2 ; cert | 3 ; reward | 4 ; vote | 5 ; propose
 *
 * @since 2.0.0
 * @category model
 */
export const RedeemerTag = /*#__PURE__*/Schema.Literal("spend", "mint", "cert", "reward", "vote", "propose").annotations({
  identifier: "Redeemer.Tag",
  title: "Redeemer Tag",
  description: "Tag indicating the context where the redeemer is used"
});
/**
 * Execution units for Plutus script execution.
 *
 * CDDL: ex_units = [mem: uint64, steps: uint64]
 *
 * @since 2.0.0
 * @category model
 */
export class ExUnits extends /*#__PURE__*/Schema.Class("Redeemer.ExUnits")({
  mem: /*#__PURE__*/Numeric.Uint64Schema.annotations({
    identifier: "Redeemer.ExUnits.Memory",
    title: "Memory Units",
    description: "Memory units consumed by script execution"
  }),
  steps: /*#__PURE__*/Numeric.Uint64Schema.annotations({
    identifier: "Redeemer.ExUnits.Steps",
    title: "CPU Steps",
    description: "CPU steps consumed by script execution"
  })
}) {
  toJSON() {
    return {
      mem: this.mem.toString(),
      steps: this.steps.toString()
    };
  }
  [Equal.symbol](that) {
    return that instanceof ExUnits && Equal.equals(this.mem, that.mem) && Equal.equals(this.steps, that.steps);
  }
  [Hash.symbol]() {
    // Only hash mem for performance
    return Hash.cached(this, Hash.hash(this.mem));
  }
}
/**
 * Redeemer for Plutus script execution based on Conway CDDL specification.
 *
 * CDDL: redeemer = [ tag, index, data, ex_units ]
 * Where:
 * - tag: redeemer_tag (0=spend, 1=mint, 2=cert, 3=reward)
 * - index: uint64 (index into the respective input/output/certificate/reward array)
 * - data: plutus_data (the actual redeemer data passed to the script)
 * - ex_units: [mem: uint64, steps: uint64] (execution unit limits)
 *
 * @since 2.0.0
 * @category model
 */
export class Redeemer extends /*#__PURE__*/Schema.Class("Redeemer")({
  tag: RedeemerTag,
  index: /*#__PURE__*/Numeric.Uint64Schema.annotations({
    identifier: "Redeemer.Index",
    title: "Redeemer Index",
    description: "Index into the respective transaction array (inputs, outputs, certificates, or rewards)"
  }),
  data: /*#__PURE__*/PlutusData.DataSchema.annotations({
    identifier: "Redeemer.Data",
    title: "Redeemer Data",
    description: "PlutusData passed to the script for validation"
  }),
  exUnits: ExUnits
}) {
  /**
   * Convert to JSON representation.
   *
   * @since 2.0.0
   * @category conversions
   */
  toJSON() {
    return {
      _tag: "Redeemer",
      tag: this.tag,
      index: this.index.toString(),
      data: this.data,
      exUnits: this.exUnits.toJSON()
    };
  }
  /**
   * Convert to string representation.
   *
   * @since 2.0.0
   * @category conversions
   */
  toString() {
    return Inspectable.format(this.toJSON());
  }
  /**
   * Custom inspect for Node.js REPL.
   *
   * @since 2.0.0
   * @category conversions
   */
  [Inspectable.NodeInspectSymbol]() {
    return this.toJSON();
  }
  /**
   * Structural equality check.
   *
   * @since 2.0.0
   * @category equality
   */
  [Equal.symbol](that) {
    return that instanceof Redeemer && this.tag === that.tag && this.index === that.index && PlutusData.equals(this.data, that.data) && Equal.equals(this.exUnits, that.exUnits);
  }
  /**
   * Hash code generation.
   * Only hashes tag and index for performance (minimal structure).
   *
   * @since 2.0.0
   * @category hashing
   */
  [Hash.symbol]() {
    return Hash.cached(this, Hash.combine(Hash.string(this.tag))(Hash.hash(this.index)));
  }
}
/**
 * Helper function to convert RedeemerTag string to CBOR integer.
 *
 * @since 2.0.0
 * @category utilities
 */
export const tagToInteger = tag => {
  switch (tag) {
    case "spend":
      return 0n;
    case "mint":
      return 1n;
    case "cert":
      return 2n;
    case "reward":
      return 3n;
    case "vote":
      return 4n;
    case "propose":
      return 5n;
  }
};
/**
 * Helper function to convert CBOR integer to RedeemerTag string.
 *
 * @since 2.0.0
 * @category utilities
 */
export const integerToTag = value => {
  switch (value) {
    case 0n:
      return "spend";
    case 1n:
      return "mint";
    case 2n:
      return "cert";
    case 3n:
      return "reward";
    case 4n:
      return "vote";
    case 5n:
      return "propose";
    default:
      throw new Error(`Invalid redeemer tag: ${value}. Must be 0 (spend), 1 (mint), 2 (cert), 3 (reward), 4 (vote), or 5 (propose)`);
  }
};
/**
 * CDDL schema for Redeemer as tuple structure.
 *
 * CDDL: redeemer = [ tag, index, data, ex_units ]
 *
 * @since 2.0.0
 * @category schemas
 */
export const CDDLSchema = /*#__PURE__*/Schema.Tuple(CBOR.Integer.annotations({
  identifier: "Redeemer.CDDL.Tag",
  title: "Redeemer Tag (CBOR)",
  description: "Redeemer tag as CBOR integer (0=spend, 1=mint, 2=cert, 3=reward)"
}), CBOR.Integer.annotations({
  identifier: "Redeemer.CDDL.Index",
  title: "Redeemer Index (CBOR)",
  description: "Index into transaction array as CBOR integer"
}), PlutusData.CDDLSchema.annotations({
  identifier: "Redeemer.CDDL.Data",
  title: "Redeemer Data (CBOR)",
  description: "PlutusData as CBOR value"
}), Schema.Tuple(CBOR.Integer, CBOR.Integer).annotations({
  identifier: "Redeemer.CDDL.ExUnits",
  title: "Execution Units (CBOR)",
  description: "Memory and CPU limits as CBOR integers"
})).annotations({
  identifier: "Redeemer.CDDLSchema",
  title: "Redeemer CDDL Schema",
  description: "CDDL representation of Redeemer as tuple"
});
/**
 * CDDL transformation schema for Redeemer.
 *
 * Transforms between CBOR tuple representation and Redeemer class instance.
 *
 * @since 2.0.0
 * @category schemas
 */
export const FromCDDL = /*#__PURE__*/Schema.transformOrFail(CDDLSchema, /*#__PURE__*/Schema.typeSchema(Redeemer), {
  strict: true,
  encode: redeemer => Effect.gen(function* () {
    const tagInteger = tagToInteger(redeemer.tag);
    const dataCBOR = yield* ParseResult.encode(PlutusData.FromCDDL)(redeemer.data);
    return [tagInteger, redeemer.index, dataCBOR, [redeemer.exUnits.mem, redeemer.exUnits.steps]];
  }),
  decode: ([tagInteger, index, dataCBOR, [mem, steps]]) => Effect.gen(function* () {
    const tag = yield* Effect.try({
      try: () => integerToTag(tagInteger),
      catch: error => new ParseResult.Type(RedeemerTag.ast, tagInteger, String(error))
    });
    const data = yield* ParseResult.decode(PlutusData.FromCDDL)(dataCBOR);
    return new Redeemer({
      tag,
      index,
      data,
      exUnits: new ExUnits({
        mem,
        steps
      })
    });
  })
});
/**
 * CBOR bytes transformation schema for Redeemer using CDDL.
 * Transforms between CBOR bytes and Redeemer using CDDL encoding.
 *
 * @since 2.0.0
 * @category schemas
 */
export const FromCBORBytes = (options = CBOR.CML_DEFAULT_OPTIONS) => Schema.compose(CBOR.FromBytes(options),
// Uint8Array → CBOR
FromCDDL // CBOR → Redeemer
).annotations({
  identifier: "Redeemer.FromCBORBytes",
  title: "Redeemer from CBOR Bytes using CDDL",
  description: "Transforms CBOR bytes to Redeemer using CDDL encoding"
});
/**
 * CBOR hex transformation schema for Redeemer using CDDL.
 * Transforms between CBOR hex string and Redeemer using CDDL encoding.
 *
 * @since 2.0.0
 * @category schemas
 */
export const FromCBORHex = (options = CBOR.CML_DEFAULT_OPTIONS) => Schema.compose(Schema.Uint8ArrayFromHex,
// string → Uint8Array
FromCBORBytes(options) // Uint8Array → Redeemer
).annotations({
  identifier: "Redeemer.FromCBORHex",
  title: "Redeemer from CBOR Hex using CDDL",
  description: "Transforms CBOR hex string to Redeemer using CDDL encoding"
});
// ============================================================================
// Constructors
// ============================================================================
/**
 * Create a spend redeemer for spending UTxO inputs.
 *
 * @since 2.0.0
 * @category constructors
 */
export const spend = (index, data, exUnits) => new Redeemer({
  tag: "spend",
  index,
  data,
  exUnits
});
/**
 * Create a mint redeemer for minting/burning tokens.
 *
 * @since 2.0.0
 * @category constructors
 */
export const mint = (index, data, exUnits) => new Redeemer({
  tag: "mint",
  index,
  data,
  exUnits
});
/**
 * Create a cert redeemer for certificate validation.
 *
 * @since 2.0.0
 * @category constructors
 */
export const cert = (index, data, exUnits) => new Redeemer({
  tag: "cert",
  index,
  data,
  exUnits
});
/**
 * Create a reward redeemer for withdrawal validation.
 *
 * @since 2.0.0
 * @category constructors
 */
export const reward = (index, data, exUnits) => new Redeemer({
  tag: "reward",
  index,
  data,
  exUnits
});
// ============================================================================
// Utilities
// ============================================================================
/**
 * Check if a redeemer is for spending inputs.
 *
 * @since 2.0.0
 * @category predicates
 */
export const isSpend = redeemer => redeemer.tag === "spend";
/**
 * Check if a redeemer is for minting/burning.
 *
 * @since 2.0.0
 * @category predicates
 */
export const isMint = redeemer => redeemer.tag === "mint";
/**
 * Check if a redeemer is for certificates.
 *
 * @since 2.0.0
 * @category predicates
 */
export const isCert = redeemer => redeemer.tag === "cert";
/**
 * Check if a redeemer is for withdrawals.
 *
 * @since 2.0.0
 * @category predicates
 */
export const isReward = redeemer => redeemer.tag === "reward";
// ============================================================================
// Transformations
// ============================================================================
/**
 * Encode Redeemer to CBOR bytes.
 *
 * @since 2.0.0
 * @category transformation
 */
export const toCBORBytes = (redeemer, options = CBOR.CML_DEFAULT_OPTIONS) => Schema.encodeSync(FromCBORBytes(options))(redeemer);
/**
 * Encode Redeemer to CBOR hex string.
 *
 * @since 2.0.0
 * @category transformation
 */
export const toCBORHex = (redeemer, options = CBOR.CML_DEFAULT_OPTIONS) => Schema.encodeSync(FromCBORHex(options))(redeemer);
/**
 * Decode Redeemer from CBOR bytes.
 *
 * @since 2.0.0
 * @category transformation
 */
export const fromCBORBytes = (bytes, options = CBOR.CML_DEFAULT_OPTIONS) => Schema.decodeSync(FromCBORBytes(options))(bytes);
/**
 * Decode Redeemer from CBOR hex string.
 *
 * @since 2.0.0
 * @category transformation
 */
export const fromCBORHex = (hex, options = CBOR.CML_DEFAULT_OPTIONS) => Schema.decodeSync(FromCBORHex(options))(hex);
// ============================================================================
// Generators
// ============================================================================
/**
 * FastCheck arbitrary for generating random RedeemerTag values.
 *
 * @since 2.0.0
 * @category generators
 */
export const arbitraryRedeemerTag = /*#__PURE__*/FastCheck.constantFrom("spend", "mint", "cert", "reward");
/**
 * FastCheck arbitrary for generating random ExUnits values.
 *
 * @since 2.0.0
 * @category generators
 */
export const arbitraryExUnits = /*#__PURE__*/FastCheck.tuple(FastCheck.bigInt({
  min: 0n,
  max: 10000000n
}),
// memory
FastCheck.bigInt({
  min: 0n,
  max: 10000000n
}) // steps
).map(([mem, steps]) => new ExUnits({
  mem,
  steps
}));
/**
 * FastCheck arbitrary for generating random Redeemer instances.
 *
 * @since 2.0.0
 * @category generators
 */
/**
 * Compute total ex_units by summing over redeemers.
 *
 * @since 2.0.0
 * @category utilities
 */
export const totalExUnits = redeemers => {
  let mem = 0n;
  let steps = 0n;
  for (const r of redeemers) {
    mem += r.exUnits.mem;
    steps += r.exUnits.steps;
  }
  return new ExUnits({
    mem,
    steps
  });
};
export const arbitrary = /*#__PURE__*/FastCheck.record({
  index: FastCheck.bigInt({
    min: 0n,
    max: 1000n
  }),
  tag: arbitraryRedeemerTag,
  data: PlutusData.arbitrary,
  exUnits: arbitraryExUnits
}).map(({
  data,
  exUnits,
  index,
  tag
}) => new Redeemer({
  tag,
  index,
  data,
  exUnits
}));
//# sourceMappingURL=Redeemer.js.map