import type { ChainId } from "./types.js";

/**
 * Thrown by transfer() for any failure that happens after the source-chain burn has
 * already succeeded. Carries burnTxHash so the caller can recover with completeMint().
 */
export class TransferError extends Error {
  readonly burnTxHash: string;
  readonly attestationHash?: string;
  readonly code?: TransferErrorCode;

  constructor(
    message: string,
    burnTxHash: string,
    attestationHash?: string,
    code?: TransferErrorCode,
  ) {
    super(message);
    this.name = "TransferError";
    this.burnTxHash = burnTxHash;
    this.attestationHash = attestationHash;
    this.code = code;
  }
}

export type TransferErrorCode = "ATTESTATION_NOT_READY" | "TRANSFER_ABORTED";

/** Thrown when Circle has not attested a confirmed burn yet. */
export class AttestationNotReadyError extends Error {
  readonly burnTxHash: string;

  constructor(message: string, burnTxHash: string) {
    super(message);
    this.name = "AttestationNotReadyError";
    this.burnTxHash = burnTxHash;
  }
}

/** Thrown when Circle's Iris service returns a non-recoverable response. */
export class IrisRequestError extends Error {
  readonly status?: number;

  constructor(message: string, status?: number) {
    super(message);
    this.name = "IrisRequestError";
    this.status = status;
  }
}

/** Thrown when a submitted transaction is not confirmed within the polling window. */
export class SubmissionTimeoutError extends Error {
  readonly hash: string;

  constructor(message: string, hash: string) {
    super(message);
    this.name = "SubmissionTimeoutError";
    this.hash = hash;
  }
}

/** Thrown before a burn when the amount exceeds destination-chain precision. */
export class AmountPrecisionError extends Error {
  readonly amount: string;
  readonly precision: number;
  readonly supportedDecimals: number;
  readonly destination: ChainId;

  constructor(
    amount: string,
    precision: number,
    supportedDecimals: number,
    destination: ChainId,
  ) {
    super(
      `Amount ${amount} has ${precision} significant decimal places, exceeding the ${supportedDecimals} decimal places supported on ${destination}`,
    );
    this.name = "AmountPrecisionError";
    this.amount = amount;
    this.precision = precision;
    this.supportedDecimals = supportedDecimals;
    this.destination = destination;
  }
}
