let lastMessageHash = 0;
let lastMessageTimeStamp = new Date();

////////////////////////////////////////////////////////
//
// translate, Log and optionally display errors
// return translated message in case caller needs it
async function dealWithError(msg, vm) {
  //Check if this is the same message again as last time within a short time span to avoid endless looping errors of same message
  //but still allow for user to repeat operation that causes error so they can view it
  const newHash = window.$gz.util.quickHash(msg);
  if (newHash == lastMessageHash) {
    const tsnow = new Date();
    //don't show the same exact message if it was just shown less than 1 second ago
    if (tsnow - lastMessageTimeStamp < 1000) return;
  }
  lastMessageHash = newHash;
  lastMessageTimeStamp = new Date();

  //translate as necessary
  msg = await window.$gz.translation.translateStringWithMultipleKeysAsync(msg);

  //In some cases the error may not be translatable, if this is not a debug run then it should show without the ?? that translating puts in keys not found
  //so it's not as weird looking to the user
  //vm may be null here so check window gz for dev
  if (!window.$gz.dev && msg.includes("??")) {
    msg = msg.replace("??", "");
  }
  window.$gz.store.commit("logItem", msg);
  if (window.$gz.dev) {
    const errMsg = "DEV MODE errorHandler.js:: Unexpected error: \r\n" + msg;
    // eslint-disable-next-line no-console
    console.error(errMsg);

    // eslint-disable-next-line no-debugger
    debugger;
  }

  //If a form instance was provided (vue instance)
  //and it can display and error then put the error into it
  if (!vm || vm.formState == undefined) {
    //Special work around to not redundantly display errors when Sockeye job fails
    // and Vue decides to throw it's own error into the mix when we've already displayed appropriate message
    if (msg.includes("Vue error") && msg.includes("Job failed")) {
      return;
    }

    //popup if no place to display it elsewise
    window.$gz.eventBus.$emit("notify-error", msg);
    return;
  }

  //should be able to display in form...
  if (vm.$sock.dev) {
    //make sure formState.appError is defined on data
    if (!window.$gz.util.has(vm, "formState.appError")) {
      throw new Error(
        "DEV ERROR errorHandler::dealWithError -> formState.appError seems to be missing from form's vue data object"
      );
    }
  }
  vm.formState.appError = msg;

  //TODO: What is this doing exactly?
  //it's related to server errors but I'm setting appError above
  //why two error properties?
  window.$gz.form.setErrorBoxErrors(vm);
}

///////////////////////////////////////////////////////////////////////////////////
// DECODE ERROR TO TEXT
// accept an unknown type of error variable
// and return human readable text
//
function decodeError(e, vm) {
  // console.log("decodeError full e object as is: ");
  // console.log(e);
  // console.log("decodeError full e object stringified: ", JSON.stringify(e));
  // console.log("decodeError is typeof:", typeof e);
  // console.log("decodeError e is instanceof Error ", e instanceof Error);
  // console.log(
  //   "decodeError e is a string already: ",
  //   window.$gz.util.isString(e)
  // );

  //already a string?
  if (window.$gz.util.isString(e)) {
    return e; //nothing to do here, already a string
  }

  if (e instanceof Error) {
    //an Error object?
    return `Error - Name:${e.name}, Message:${e.message}`;
  }

  if (
    e == null ||
    e == "" ||
    (typeof e === "object" && Object.keys(e).length === 0)
  ) {
    return `errorHandler::decodeError - Error is unknown / empty (e:${e})`;
  }

  //API error object or error RESPONSE object?
  if (e.error || e.code) {
    let err = null;
    //could be the error RESPONSE or it could be the error object *inside* the error response so sort out here
    if (e.error) {
      //it's the entire resopnse object
      err = e.error;
    } else {
      //it's the inner error object only
      err = e;
    }
    let msg = "";
    if (err.code) {
      msg += err.code;
      msg += " - ";
      if (vm) {
        msg += vm.$sock.t("ErrorAPI" + err.code);
      }
      msg += "\n";
    }
    if (err.target) {
      msg += err.target;
      msg += "\n";
    }

    if (err.message && !err.message.startsWith("ErrorAPI")) {
      //errapi already dealt with above no need to repeat it here
      msg += err.message;
      msg += "\n";
    }

    if (err.details) {
      err.details.forEach(z => {
        let zerror = null;
        if (z.error) {
          zerror = z.error + " - ";
        }
        msg += `${zerror}${z.message}\n`;
      });
    }

    //console.log("errorhandler:decodeError returning message:", msg);

    return msg;
  }

  //Javascript Fetch API Response object?
  if (e instanceof Response) {
    return `http error: ${e.statusText} - ${e.status} Url: ${e.url}`;
  }

  //last resort
  return JSON.stringify(e);
}
export default {
  handleGeneralError(message, source, lineno, colno, error) {
    let msg = "General error: \n" + message;
    if (source) {
      msg += "\nsource: " + source;
    }
    if (lineno) {
      msg += "\nlineno: " + lineno;
    }
    if (colno) {
      msg += "\ncolno: " + colno;
    }
    if (error) {
      if (typeof error === "object") {
        error = JSON.stringify(error);
      }
      msg += "\nerror: " + error;
    }
    dealWithError(msg);
  },
  handleVueError(err, vm, info) {
    let msg = "Vue error: \n" + decodeError(err, vm);
    if (err.fileName) {
      msg += "\nfilename: " + err.fileName;
    }
    if (err.lineNumber) {
      msg += "\nlineNumber: " + err.lineNumber;
    }
    if (info) {
      msg += "\ninfo: " + info;
    }
    if (err.stack) {
      msg += "\nSTACK:\n " + err.stack;
    }
    dealWithError(msg, vm);
  },
  handleVueWarning(wmsg, vm, trace) {
    let msg = "Vue warning: \n" + decodeError(wmsg, vm);
    if (trace) {
      msg += "\ntrace: " + trace;
    }
    dealWithError(msg, vm);
  },
  /////////////////////////////////////////////////
  //  translate, log and return error
  //
  handleFormError(err, vm) {
    if (window.$gz.dev) {
      console.trace(err);
    }
    //called inside forms when things go unexpectedly wrong
    dealWithError(decodeError(err, vm), vm);
  },
  /////////////////////////////////////////////////
  //  decode error into string suitable to display
  //
  errorToString(err, vm) {
    //called inside forms when things go unexpectedly wrong
    return decodeError(err, vm);
  }
};
/*
ERROR CODES USED:
Client error codes are all in the range of E16 to E999
Server error codes are all in the range of E1000 to E1999
API specific (logic) error codes are all in the range of 2000 to 3000

CLIENT ERROR CODES:
E16 - ErrorUserNotAuthenticated
E17 - ErrorServerUnresponsive
E18 - Misc error without a translation key, unexpected throws etc or api error during server call, details in the message / Any error without a translation key defined basically

*/
