import deepCopy from "@stdlib/utils-copy";
import { GhostError } from "./GhostError";
import * as errors from "./errors";
const errorsWithBase = { ...errors, GhostError };
const _private = {
  serialize(err) {
    try {
      return {
        id: err.id,
        status: err.statusCode,
        code: err.code || err.errorType,
        title: err.name,
        detail: err.message,
        meta: {
          context: err.context,
          help: err.help,
          errorDetails: err.errorDetails,
          level: err.level,
          errorType: err.errorType
        }
      };
    } catch (error) {
      return {
        detail: "Something went wrong."
      };
    }
  },
  deserialize(obj) {
    return {
      id: obj.id,
      message: obj.detail || obj.error_description || obj.message,
      statusCode: obj.status,
      code: obj.code || obj.error,
      level: obj.meta && obj.meta.level,
      help: obj.meta && obj.meta.help,
      context: obj.meta && obj.meta.context
    };
  },
  /**
   * @description Serialize error instance into oauth format.
   *
   * @see https://tools.ietf.org/html/rfc6749#page-45
   *
   * To not loose any error data when sending errors between internal services, we use the suggested OAuth properties and add ours as well.
   */
  OAuthSerialize(err) {
    const matchTable = {
      [errors.NoPermissionError.name]: "access_denied",
      [errors.MaintenanceError.name]: "temporarily_unavailable",
      [errors.BadRequestError.name]: "invalid_request",
      [errors.ValidationError.name]: "invalid_request",
      default: "server_error"
    };
    const { detail, code, ...properties } = _private.serialize(err);
    return {
      error: err.code || matchTable[err.name] || "server_error",
      error_description: err.message,
      ...properties
    };
  },
  /**
   * @description Deserialize oauth error format into GhostError instance.
   * @constructor
   */
  OAuthDeserialize(errorFormat) {
    try {
      return new errorsWithBase[errorFormat.title || errorFormat.name || errors.InternalServerError.name](_private.deserialize(errorFormat));
    } catch (err) {
      return new errors.InternalServerError({
        errorType: errorFormat.title || errorFormat.name,
        ..._private.deserialize(errorFormat)
      });
    }
  },
  /**
   * @description Serialize GhostError instance into jsonapi.org format.
   * @param err
   * @return {Object}
   */
  JSONAPISerialize(err) {
    const errorFormat = {
      errors: [_private.serialize(err)]
    };
    errorFormat.errors[0].source = {};
    if (err.property) {
      errorFormat.errors[0].source.pointer = "/data/attributes/" + err.property;
    }
    return errorFormat;
  },
  /**
   * @description Deserialize JSON api format into GhostError instance.
   */
  JSONAPIDeserialize(errorFormat) {
    errorFormat = errorFormat.errors && errorFormat.errors[0] || {};
    let internalError;
    try {
      internalError = new errorsWithBase[errorFormat.title || errorFormat.name || errors.InternalServerError.name](_private.deserialize(errorFormat));
    } catch (err) {
      internalError = new errors.InternalServerError({
        errorType: errorFormat.title || errorFormat.name,
        ..._private.deserialize(errorFormat)
      });
    }
    if (errorFormat.source && errorFormat.source.pointer) {
      internalError.property = errorFormat.source.pointer.split("/")[3];
    }
    return internalError;
  }
};
function serialize(err, options) {
  options = options || { format: "jsonapi" };
  let errorFormat = {};
  try {
    if (options.format === "jsonapi") {
      errorFormat = _private.JSONAPISerialize(err);
    } else {
      errorFormat = _private.OAuthSerialize(err);
    }
  } catch (error) {
    errorFormat.message = "Something went wrong.";
  }
  return errorFormat;
}
;
function deserialize(errorFormat) {
  let internalError = {};
  if (errorFormat.errors) {
    internalError = _private.JSONAPIDeserialize(errorFormat);
  } else {
    internalError = _private.OAuthDeserialize(errorFormat);
  }
  return internalError;
}
;
function prepareStackForUser(error) {
  const stackbits = error.stack?.split(/\n/) || [];
  const hideStack = "hideStack" in error && error.hideStack;
  if (hideStack) {
    stackbits.splice(1, stackbits.length - 1);
  } else {
    stackbits.splice(1, 0, `Stack Trace:`);
  }
  if ("help" in error && error.help) {
    stackbits.splice(1, 0, `${error.help}`);
  }
  if ("context" in error && error.context) {
    stackbits.splice(1, 0, `${error.context}`);
  }
  const errorClone = deepCopy(error);
  errorClone.stack = stackbits.join("\n");
  return errorClone;
}
;
function isGhostError(err) {
  const errorName = GhostError.name;
  const legacyErrorName = "IgnitionError";
  const recursiveIsGhostError = function recursiveIsGhostError2(obj) {
    if (!obj || !obj.name) {
      return false;
    }
    if (obj.name === errorName || obj.name === legacyErrorName) {
      return true;
    }
    return recursiveIsGhostError2(Object.getPrototypeOf(obj));
  };
  return recursiveIsGhostError(err.constructor);
}
;
export {
  deserialize,
  isGhostError,
  prepareStackForUser,
  serialize
};
