import { Effect as Eff, Equal, FastCheck, Hash, Inspectable, ParseResult, Schema } from "effect";
import * as AuxiliaryData from "./AuxiliaryData.js";
import * as CBOR from "./CBOR.js";
import * as TransactionBody from "./TransactionBody.js";
import * as TransactionWitnessSet from "./TransactionWitnessSet.js";
/**
 * Transaction based on Conway CDDL specification
 *
 * CDDL: transaction =
 *   [transaction_body, transaction_witness_set, bool, auxiliary_data / nil]
 *
 * @since 2.0.0
 * @category model
 */
export class Transaction extends /*#__PURE__*/Schema.TaggedClass()("Transaction", {
  body: TransactionBody.TransactionBody,
  witnessSet: TransactionWitnessSet.TransactionWitnessSet,
  isValid: Schema.Boolean,
  auxiliaryData: /*#__PURE__*/Schema.NullOr(AuxiliaryData.AuxiliaryData)
}) {
  toJSON() {
    return {
      _tag: this._tag,
      body: this.body.toJSON(),
      witnessSet: this.witnessSet.toJSON(),
      isValid: this.isValid,
      auxiliaryData: this.auxiliaryData?.toJSON() ?? null
    };
  }
  toString() {
    return Inspectable.format(this.toJSON());
  }
  [Inspectable.NodeInspectSymbol]() {
    return this.toJSON();
  }
  [Equal.symbol](that) {
    return that instanceof Transaction && Equal.equals(this.body, that.body) && Equal.equals(this.witnessSet, that.witnessSet) && this.isValid === that.isValid && Equal.equals(this.auxiliaryData, that.auxiliaryData);
  }
  [Hash.symbol]() {
    return Hash.cached(this, Hash.combine(Hash.combine(Hash.combine(Hash.hash(this.body))(Hash.hash(this.witnessSet)))(Hash.hash(this.isValid)))(Hash.hash(this.auxiliaryData)));
  }
}
/**
 * Conway CDDL schema for Transaction tuple structure.
 *
 * CDDL: transaction = [transaction_body, transaction_witness_set, bool, auxiliary_data / nil]
 */
export const CDDLSchema = /*#__PURE__*/Schema.declare(input => Array.isArray(input)).annotations({
  identifier: "Transaction.CDDLSchema",
  description: "Transaction tuple structure"
});
/**
 * Transform between CDDL tuple and Transaction class.
 */
export const FromCDDL = /*#__PURE__*/Schema.transformOrFail(CDDLSchema, /*#__PURE__*/Schema.typeSchema(Transaction), {
  strict: true,
  encode: tx => Eff.gen(function* () {
    const bodyReadonly = yield* ParseResult.encode(TransactionBody.FromCDDL)(tx.body);
    const witnessReadonly = yield* ParseResult.encode(TransactionWitnessSet.FromCDDL)(tx.witnessSet);
    // Ensure mutable Map instances for tuple A-type compatibility
    const body = new Map(bodyReadonly.entries());
    const witnessSet = new Map(witnessReadonly.entries());
    const isValid = tx.isValid;
    const auxiliaryData = tx.auxiliaryData === null ? null : yield* ParseResult.encode(AuxiliaryData.FromCDDL)(tx.auxiliaryData);
    const result = [body, witnessSet, isValid, auxiliaryData];
    return result;
  }),
  decode: tuple => Eff.gen(function* () {
    const [bodyCDDL, witnessSetCDDL, isValid, aux] = tuple;
    const body = yield* ParseResult.decode(TransactionBody.FromCDDL)(bodyCDDL);
    const witnessSet = yield* ParseResult.decode(TransactionWitnessSet.FromCDDL)(witnessSetCDDL);
    const auxiliaryData = aux === null ? null : yield* ParseResult.decodeUnknownEither(AuxiliaryData.FromCDDL)(aux);
    return new Transaction({
      body,
      witnessSet,
      isValid,
      auxiliaryData
    }, {
      disableValidation: true
    });
  })
});
/**
 * CBOR bytes transformation schema for Transaction.
 */
export const FromCBORBytes = (options = CBOR.CML_DEFAULT_OPTIONS) => Schema.compose(CBOR.FromBytes(options), FromCDDL).annotations({
  identifier: "Transaction.FromCBORBytes",
  description: "Decode Transaction from CBOR bytes per Conway CDDL"
});
/**
 * CBOR hex transformation schema for Transaction.
 */
export const FromCBORHex = (options = CBOR.CML_DEFAULT_OPTIONS) => Schema.compose(CBOR.FromHex(options), FromCDDL).annotations({
  identifier: "Transaction.FromCBORHex",
  description: "Decode Transaction from CBOR hex per Conway CDDL"
});
// ============================================================================
// Parsing / Encoding Functions
// ============================================================================
/**
 * Internal cache that associates a Transaction with the CBORFormat tree
 * captured when it was decoded. This lets the default `toCBORHex` /
 * `toCBORBytes` path automatically preserve the original CBOR encoding
 * (indefinite-length arrays, map key ordering, etc.) without requiring
 * callers to use the explicit WithFormat API.
 *
 * WeakMap ensures the format is garbage-collected with the Transaction.
 */
const formatCache = /*#__PURE__*/new WeakMap();
export const fromCBORBytes = (bytes, options = CBOR.CML_DEFAULT_OPTIONS) => {
  // When caller passes non-default codec options, honor them via the legacy
  // decode path and skip format caching — custom options typically imply the
  // caller wants re-canonicalization, which is incompatible with preserving
  // the original encoding.
  if (options !== CBOR.CML_DEFAULT_OPTIONS) {
    return Schema.decodeSync(FromCBORBytes(options))(bytes);
  }
  const {
    format,
    value
  } = fromCBORBytesWithFormat(bytes);
  formatCache.set(value, format);
  return value;
};
export const fromCBORHex = (hex, options = CBOR.CML_DEFAULT_OPTIONS) => {
  if (options !== CBOR.CML_DEFAULT_OPTIONS) {
    return Schema.decodeSync(FromCBORHex(options))(hex);
  }
  const {
    format,
    value
  } = fromCBORHexWithFormat(hex);
  formatCache.set(value, format);
  return value;
};
/**
 * Parse a Transaction from CBOR bytes and return the root format tree.
 *
 * @since 2.0.0
 * @category parsing
 */
export const fromCBORBytesWithFormat = bytes => {
  const decoded = CBOR.fromCBORBytesWithFormat(bytes);
  const value = Schema.decodeSync(FromCDDL)(decoded.value);
  return {
    value,
    format: decoded.format
  };
};
/**
 * Parse a Transaction from CBOR hex string and return the root format tree.
 *
 * @since 2.0.0
 * @category parsing
 */
export const fromCBORHexWithFormat = hex => {
  const decoded = CBOR.fromCBORHexWithFormat(hex);
  const value = Schema.decodeSync(FromCDDL)(decoded.value);
  return {
    value,
    format: decoded.format
  };
};
export const toCBORBytes = (data, options = CBOR.CML_DEFAULT_OPTIONS) => {
  // Non-default options signal an explicit re-encode request — bypass the
  // cached format so the caller's options actually take effect.
  if (options !== CBOR.CML_DEFAULT_OPTIONS) {
    return Schema.encodeSync(FromCBORBytes(options))(data);
  }
  const cached = formatCache.get(data);
  if (cached) return toCBORBytesWithFormat(data, cached);
  return Schema.encodeSync(FromCBORBytes(options))(data);
};
export const toCBORHex = (data, options = CBOR.CML_DEFAULT_OPTIONS) => {
  if (options !== CBOR.CML_DEFAULT_OPTIONS) {
    return Schema.encodeSync(FromCBORHex(options))(data);
  }
  const cached = formatCache.get(data);
  if (cached) return toCBORHexWithFormat(data, cached);
  return Schema.encodeSync(FromCBORHex(options))(data);
};
/**
 * Convert a Transaction to CBOR bytes using an explicit root format tree.
 *
 * @since 2.0.0
 * @category encoding
 */
export const toCBORBytesWithFormat = (data, format) => {
  const cborTuple = Schema.encodeSync(FromCDDL)(data);
  return CBOR.toCBORBytesWithFormat(cborTuple, format);
};
/**
 * Convert a Transaction to CBOR hex string using an explicit root format tree.
 *
 * @since 2.0.0
 * @category encoding
 */
export const toCBORHexWithFormat = (data, format) => {
  const cborTuple = Schema.encodeSync(FromCDDL)(data);
  return CBOR.toCBORHexWithFormat(cborTuple, format);
};
// ============================================================================
// Witness merging via WithFormat round-trip
//
// Decode the full transaction with format preservation, merge witnesses at
// the domain level, then re-encode using the captured format tree. The format
// tree ensures body, redeemers, scripts, and all other entries maintain their
// original encoding — preserving txId and scriptDataHash.
//
// Reconciliation handles structural changes gracefully:
// - New map entries (key 0 absent → added) get default encoding
// - Extended arrays (more witnesses) encode extra children minimally
// - Surviving entries replay their captured format exactly
// ============================================================================
/**
 * Merge wallet vkey witnesses into a transaction, preserving CBOR encoding.
 *
 * Uses the WithFormat round-trip: decode with format capture, mutate at the
 * domain level, re-encode with the original format tree. Body encoding,
 * redeemer bytes, map key ordering, and all non-witness data are preserved
 * through the format tree reconciliation.
 *
 * `options` applies only to parsing the wallet witness set bytes. Transaction
 * decoding and re-encoding are governed by the captured format tree, making
 * codec options irrelevant for the transaction round-trip path.
 *
 * @since 2.0.0
 * @category encoding
 */
export const addVKeyWitnessesBytes = (txBytes, walletWitnessSetBytes, options = CBOR.CML_DEFAULT_OPTIONS) => {
  // Decode wallet witness set to extract vkey witnesses
  const walletWs = TransactionWitnessSet.fromCBORBytes(walletWitnessSetBytes, options);
  const walletVkeys = walletWs.vkeyWitnesses ?? [];
  if (walletVkeys.length === 0) return txBytes;
  // Decode transaction with full format preservation
  const {
    format,
    value: tx
  } = fromCBORBytesWithFormat(txBytes);
  // Add witnesses at the domain level
  const merged = addVKeyWitnesses(tx, walletVkeys);
  // Re-encode using the captured format tree — reconciliation handles
  // the added/extended witness entries while preserving everything else
  return toCBORBytesWithFormat(merged, format);
};
/**
 * Hex variant of `addVKeyWitnessesBytes`.
 *
 * @since 2.0.0
 * @category encoding
 */
export const addVKeyWitnessesHex = (txHex, walletWitnessSetHex, options = CBOR.CML_DEFAULT_OPTIONS) => {
  const txBytes = Schema.decodeSync(Schema.Uint8ArrayFromHex)(txHex);
  const wsBytes = Schema.decodeSync(Schema.Uint8ArrayFromHex)(walletWitnessSetHex);
  const result = addVKeyWitnessesBytes(txBytes, wsBytes, options);
  return Schema.encodeSync(Schema.Uint8ArrayFromHex)(result);
};
// ============================================================================
// Raw body bytes extraction
// ============================================================================
/** Skip a CBOR item header and return its byte width. */
const cborHeaderSize = (data, offset) => {
  const additionalInfo = data[offset] & 0x1f;
  if (additionalInfo < 24) return 1;
  if (additionalInfo === CBOR.CBOR_ADDITIONAL_INFO.DIRECT) return 2;
  if (additionalInfo === CBOR.CBOR_ADDITIONAL_INFO.UINT16) return 3;
  if (additionalInfo === CBOR.CBOR_ADDITIONAL_INFO.UINT32) return 5;
  if (additionalInfo === CBOR.CBOR_ADDITIONAL_INFO.UINT64) return 9;
  if (additionalInfo === CBOR.CBOR_ADDITIONAL_INFO.INDEFINITE) return 1;
  throw new CBOR.CBORError({
    message: `Unsupported additional info: ${additionalInfo}`
  });
};
/**
 * Extract the original body bytes from a raw transaction CBOR byte array.
 * A Cardano transaction is a 4-element CBOR array: `[body, witnessSet, isValid, auxiliaryData]`.
 * This returns the raw body bytes without decoding/re-encoding, preserving the exact CBOR encoding.
 *
 * @since 2.0.0
 * @category encoding
 */
export const extractBodyBytes = txBytes => {
  const arrHdr = cborHeaderSize(txBytes, 0);
  const {
    newOffset: bodyEnd
  } = CBOR.decodeItemWithOffset(txBytes, arrHdr);
  return txBytes.subarray(arrHdr, bodyEnd);
};
// ============================================================================
// Domain-level witness addition
// ============================================================================
/**
 * Add VKey witnesses to a transaction at the domain level.
 *
 * This creates a new Transaction with the additional witnesses merged in.
 * All encoding metadata (body bytes, redeemers format, witness map structure)
 * is preserved so that txId and scriptDataHash remain stable.
 *
 * @since 2.0.0
 * @category encoding
 */
export const addVKeyWitnesses = (tx, witnesses) => {
  if (witnesses.length === 0) return tx;
  const oldWs = tx.witnessSet;
  const newWs = new TransactionWitnessSet.TransactionWitnessSet({
    ...oldWs,
    vkeyWitnesses: [...(oldWs.vkeyWitnesses ?? []), ...witnesses]
  }, {
    disableValidation: true
  });
  const result = new Transaction({
    body: tx.body,
    witnessSet: newWs,
    isValid: tx.isValid,
    auxiliaryData: tx.auxiliaryData
  }, {
    disableValidation: true
  });
  // Transfer cached format so toCBORHex/toCBORBytes preserves encoding
  const fmt = formatCache.get(tx);
  if (fmt) formatCache.set(result, fmt);
  return result;
};
// ============================================================================
// Arbitrary (FastCheck)
// ============================================================================
export const arbitrary = /*#__PURE__*/FastCheck.record({
  body: TransactionBody.arbitrary,
  witnessSet: TransactionWitnessSet.arbitrary,
  isValid: FastCheck.boolean(),
  auxiliaryData: FastCheck.option(AuxiliaryData.arbitrary, {
    nil: null
  }).map(a => a === undefined ? null : a)
}).map(r => new Transaction(r));
//# sourceMappingURL=Transaction.js.map