import process from "node:process";
import * as Hapi from "@hapi/hapi";
import Joi from "joi";
import * as Hoek from "@hapi/hoek";
import * as Boom from "@hapi/boom";

export interface HapiValidationError extends Joi.ValidationError {
  output: {
    statusCode: number;
    headers: Hapi.Utils.Dictionary<string | string[]>;
    payload: {
      statusCode: number;
      error: string;
      message?: string;
      validation: {
        source: string;
        keys: string[];
      };
    };
  };
}
export function defaultValidationErrorHandler(
  request: Hapi.Request,
  h: Hapi.ResponseToolkit,
  err?: Error
): Hapi.Lifecycle.ReturnValue {
  // Newer versions of Joi don't format the key for missing params the same way. This shim
  // provides backwards compatibility. Unfortunately, Joi doesn't export it's own Error class
  // in JS so we have to rely on the `name` key before we can cast it.
  //
  // The Hapi code we're 'overwriting' can be found here:
  //     https://github.com/hapijs/hapi/blob/master/lib/validation.js#L102
  if (err && err.name === "ValidationError" && err.hasOwnProperty("output")) {
    const validationError: HapiValidationError = err as HapiValidationError;
    const validationKeys: string[] = [];

    validationError.details.forEach((detail) => {
      if (detail.path.length > 0) {
        validationKeys.push(Hoek.escapeHtml(detail.path.join(".")));
      } else {
        // If no path, use the value sigil to signal the entire value had an issue.
        validationKeys.push("value");
      }
    });

    validationError.output.payload.validation.keys = validationKeys;
  }

  throw err;
}

export const validatingFailAction = async (
  request: Hapi.Request,
  h: Hapi.ResponseToolkit,
  err: Error
): Promise<void> => {
  if (process.env.NODE_ENV === "production") {
    throw Boom.badRequest("Invalid request payload input");
  } else {
    defaultValidationErrorHandler(request, h, err);
  }
};
