/**
 * Used by Flavor to mark a type in a readable way.
 */
export interface Flavoring<FlavorT> {
  _type?: FlavorT;
}
/**
 *
 * Create a "flavored" version of a type. TypeScript will disallow mixing
 * flavors, but will allow unflavored values of that type to be passed in where
 * a flavored version is expected. This is a less restrictive form of branding.
 *
 */
export type Flavor<T, FlavorT> = T & Flavoring<FlavorT>;

export type UUID = Flavor<string, "A UUID">;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const deepFreeze = (o: unknown): any => {
  Object.freeze(o);

  const oIsFunction = typeof o === "function";
  const hasOwnProp = Object.prototype.hasOwnProperty;

  Object.getOwnPropertyNames(o).forEach((prop) => {
    if (
      hasOwnProp.call(o, prop) &&
      (oIsFunction
        ? prop !== "caller" && prop !== "callee" && prop !== "arguments"
        : true) &&
      o[prop] !== null &&
      (typeof o[prop] === "object" || typeof o[prop] === "function") &&
      !Object.isFrozen(o[prop])
    ) {
      deepFreeze(o[prop]);
    }
  });

  return o;
};
