export class BaseError extends Error {
  constructor(
    message: string,
    public override readonly cause?: unknown,
  ) {
    super(message);
    this.name = this.constructor.name;
    Object.setPrototypeOf(this, BaseError.prototype);
  }

  // Format the error for logging/debugging
  public toJSON(): Record<string, unknown> {
    return {
      name: this.name,
      message: this.message,
      cause: this.formatCause(),
    };
  }

  private formatCause(): unknown {
    if (this.cause instanceof Error) {
      if ('toJSON' in this.cause && typeof this.cause.toJSON === 'function') {
        return this.cause.toJSON();
      }
      return {
        name: this.cause.name,
        message: this.cause.message,
        ...(this.cause.stack && { stack: this.cause.stack }),
      };
    }
    return this.cause;
  }
}

export class ValidationError extends BaseError {
  constructor(message: string, cause?: unknown) {
    super(message, cause);
    Object.setPrototypeOf(this, ValidationError.prototype);
  }
}

export class NetworkError extends BaseError {
  constructor(message: string, cause?: unknown) {
    super(message, cause);
    Object.setPrototypeOf(this, NetworkError.prototype);
  }
}

export class InsufficientBalanceError extends BaseError {
  constructor(message: string, cause?: unknown) {
    super(message, cause);
    Object.setPrototypeOf(this, InsufficientBalanceError.prototype);
  }
}

export class InsufficientAllowanceError extends BaseError {
  constructor(message: string, cause?: unknown) {
    super(message, cause);
    Object.setPrototypeOf(this, InsufficientAllowanceError.prototype);
  }
}

export function handleError(error: unknown): BaseError {
  if (error instanceof BaseError) {
    return error;
  }

  if (error instanceof Error) {
    return new BaseError(error.message, error);
  }

  return new BaseError(
    typeof error === 'string' ? error : 'Unknown error occurred',

    error,
  );
}
