import { ObjectValue, Value } from "../types";

export default function throwIfObjectValueIsInvalid(value: ObjectValue): void {
  if (typeof value !== "object") {
    throw new Error(`value with type ${typeof value} is not an object`);
  }
  throwIfValueIsInvalid(value, []);
}

function throwIfValueIsInvalid(value: Value, path: string[]): void {
  if (value === null || value === undefined) {
    throw new Error(`null value is not allowed ${pathMessage(path)}`);
  }
  if (Array.isArray(value)) {
    if (value.length === 0) {
      return;
    }
    const itemType = typeof value[0];

    value.forEach((itemValue, index) => {
      if (typeof itemValue !== itemType) {
        throw new Error(
          `item has different type than the first item ${pathMessage([...path, index.toString()])}`
        );
      }
      throwIfValueIsInvalid(itemValue, [...path, index.toString()]);
    });
    return;
  }
  const valueType = typeof value;

  switch (valueType) {
    case "boolean":
    case "number":
    case "string":
      return;
    case "object":
      Object.entries(value).forEach(([fieldName, fieldValue]) => {
        throwIfValueIsInvalid(fieldValue, [...path, fieldName]);
      });
      break;

    default:
      throw new Error(
        `value with type ${valueType} is not allowed ${pathMessage(path)}`
      );
  }
}

function pathMessage(path: string[]): string {
  return path && path.length > 0 ? `at ${path.join(".")}` : "";
}
