
function stringify (v: any): string { // eslint-disable-line @typescript-eslint/no-explicit-any
  if (typeof v === "number" && !isFinite(v)) {
    if (isNaN(v)) {
      return "NaN";
    }
    return v > 0 ? "Infinity" : "-Infinity";
  }
  return JSON.stringify(v);
}

export function mkErrorMessage(path: string, value: any, expectedType: string) {  // eslint-disable-line
  return `Invalid value ${stringify(
value
)} for ${path} - Expected a value of type ${expectedType}.`;
}

export default class CfgErrors {
  errors: string[] = [];
  prefix: string;

  constructor (prefix = "config.networks") {
    this.prefix = prefix;
  }

  public push(net: string, field: string, val: any, expectedType: string) {  // eslint-disable-line
    this.errors.push(mkErrorMessage(
      `${this.prefix}.${net}.${field}`,
      val,
      expectedType));
  }

  public appendErrors (errors: string[]): void {
    for (const e of errors) { this.errors.push(e); }
  }

  public isEmpty (): boolean {
    return this.errors.length === 0;
  }

  public toString (): string {
    return this.errors.join("\n  * ");
  }

  public putter (net: string, field: string): ErrorPutter {
    return new ErrorPutter(this, net, field);
  }
}

export class ErrorPutter {
  errs: CfgErrors;
  net: string;
  field: string;
  public isEmpty = true;

  constructor (errs: CfgErrors, net: string, field: string) {
    this.errs = errs;
    this.net = net;
    this.field = field;
  }

  // wraps CfgErrors.put and always returns false.
  public push(field: string, val: any, expectedType: string): boolean { // eslint-disable-line
    if (field === "") { field = this.field; } else { field = this.field + "." + field; }
    this.errs.push(this.net, field, val, expectedType);
    this.isEmpty = false;
    return false;
  }
}
