/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Duration } from "#time/Duration.js";
import { Time } from "#time/Time.js";
import { Millis } from "#time/TimeUnit.js";
import { Bytes } from "#util/Bytes.js";
import { asError } from "#util/Error.js";
import { DiagnosticPresentation } from "./DiagnosticPresentation.js";
import { LogLevel } from "./LogLevel.js";
let errorCollector;
function Diagnostic(presentation, value) {
  return {
    [Diagnostic.presentation]: presentation,
    [Diagnostic.value]: value
  };
}
((Diagnostic2) => {
  Diagnostic2.Presentation = DiagnosticPresentation;
  Diagnostic2.presentation = DiagnosticPresentation.presentation;
  Diagnostic2.value = DiagnosticPresentation.value;
  function presentationOf(diagnostic) {
    return diagnostic?.[Diagnostic2.presentation];
  }
  Diagnostic2.presentationOf = presentationOf;
  function valueOf(diagnostic) {
    return diagnostic?.[Diagnostic2.value];
  }
  Diagnostic2.valueOf = valueOf;
  function Context() {
    let errorsCollected;
    const errorsReported = /* @__PURE__ */ new WeakSet();
    const thisErrorCollector = (error2) => {
      if (errorsReported.has(error2)) {
        return true;
      }
      if (errorsCollected) {
        errorsCollected.push(error2);
      } else {
        errorsCollected = [error2];
      }
      return false;
    };
    return {
      run(fn) {
        const originalErrorCollector = errorCollector;
        try {
          errorCollector = thisErrorCollector;
          const result = fn();
          if (errorsCollected) {
            for (const error2 of errorsCollected) {
              errorsReported.add(error2);
            }
            errorsCollected = void 0;
          }
          return result;
        } finally {
          errorCollector = originalErrorCollector;
        }
      }
    };
  }
  Diagnostic2.Context = Context;
  function message(value2) {
    const { now, level, facility, prefix: nestingPrefix, values } = value2;
    return {
      [Diagnostic2.presentation]: Diagnostic2.Presentation.Message,
      now: now ?? /* @__PURE__ */ new Date(),
      level: level ?? LogLevel.INFO,
      facility: facility ?? "Diagnostic",
      prefix: nestingPrefix ?? "",
      values: values ?? []
    };
  }
  Diagnostic2.message = message;
  function strong(value2) {
    return Diagnostic2("strong", value2);
  }
  Diagnostic2.strong = strong;
  function weak(value2) {
    return Diagnostic2("weak", value2);
  }
  Diagnostic2.weak = weak;
  function flag(value2) {
    return Diagnostic2("flag", value2);
  }
  Diagnostic2.flag = flag;
  function via(value2) {
    if (Diagnostic2.presentationOf(value2)) {
      return value2;
    }
    const via2 = new String(value2);
    Object.defineProperty(via2, Diagnostic2.presentation, { value: Diagnostic2.Presentation.Via });
    return via2;
  }
  Diagnostic2.via = via;
  function added(value2) {
    return Diagnostic2("added", value2);
  }
  Diagnostic2.added = added;
  function deleted(value2) {
    return Diagnostic2("deleted", value2);
  }
  Diagnostic2.deleted = deleted;
  function node(icon, label, detail) {
    const result = [icon, Diagnostic2.strong(label)];
    if (detail?.self !== void 0) {
      result.push(detail.self);
    }
    if (detail?.children !== void 0) {
      result.push(Diagnostic2.list(detail.children));
    }
    return result;
  }
  Diagnostic2.node = node;
  function list(value2) {
    return Diagnostic2("list", value2);
  }
  Diagnostic2.list = list;
  function squash(...values) {
    return Diagnostic2("squash", values);
  }
  Diagnostic2.squash = squash;
  function dict(entries, suppressUndefinedValues = true) {
    const result = {
      ...entries,
      [Diagnostic2.presentation]: "dictionary"
    };
    if (suppressUndefinedValues) {
      for (const key in result) {
        if (result[key] === void 0) {
          delete result[key];
        }
      }
    }
    return result;
  }
  Diagnostic2.dict = dict;
  function error(error2) {
    return formatError(error2);
  }
  Diagnostic2.error = error;
  function errorMessage(details) {
    const diagnostic = Array();
    format(details);
    return Diagnostic2("error", diagnostic);
    function format({ id, message: message2, path, cause }) {
      if (id) {
        diagnostic.push(Diagnostic2.squash("[", Diagnostic2.strong(id), "]"));
      }
      if (path) {
        diagnostic.push(Diagnostic2.squash(path, ":"));
      }
      const hasCause = cause !== void 0;
      if (hasCause) {
        diagnostic.push(Diagnostic2.squash(message2, ":"));
        format(asError(cause));
      } else {
        diagnostic.push(message2);
      }
    }
  }
  Diagnostic2.errorMessage = errorMessage;
  function lifecycle(status, value2) {
    return Diagnostic2(status, value2);
  }
  Diagnostic2.lifecycle = lifecycle;
  function lifecycleList(map) {
    return Object.entries(map).map(([label, status]) => Diagnostic2(status, label));
  }
  Diagnostic2.lifecycleList = lifecycleList;
  function elapsed() {
    return {
      startedAt: Time.nowUs,
      get time() {
        return Millis(Time.nowUs - this.startedAt);
      },
      toString() {
        return Duration.format(this.time);
      },
      get [Diagnostic2.value]() {
        return this.toString();
      }
    };
  }
  Diagnostic2.elapsed = elapsed;
  function upgrade(value2, diagnostic) {
    switch (typeof value2) {
      case "boolean":
        value2 = new Boolean(value2);
        break;
      case "number":
        value2 = new Number(value2);
        break;
      case "string":
        value2 = new String(value2);
        break;
    }
    if (typeof diagnostic === "function") {
      Object.defineProperty(value2, Diagnostic2.value, { get: diagnostic });
    } else {
      Object.defineProperty(value2, Diagnostic2.value, { value: diagnostic });
    }
    return value2;
  }
  Diagnostic2.upgrade = upgrade;
  function hex(value2, padding) {
    const hexString = value2.toString(16);
    if (padding !== void 0) {
      return hexString.padStart(padding, "0");
    }
    return `0x${hexString}`;
  }
  Diagnostic2.hex = hex;
  function json(data) {
    return JSON.stringify(data, (_, value2) => {
      if (typeof value2 === "bigint") {
        return value2.toString();
      }
      if (Bytes.isBytes(value2)) {
        return Bytes.toHex(value2);
      }
      if (value2 === void 0) {
        return "undefined";
      }
      return value2;
    });
  }
  Diagnostic2.json = json;
  function asFlags(flags) {
    return Diagnostic2.flag(Diagnostic2.toFlagString(flags));
  }
  Diagnostic2.asFlags = asFlags;
  function toFlagString(flags) {
    return Object.entries(flags).filter(([, value2]) => !!value2).map(([key]) => key).join(" ");
  }
  Diagnostic2.toFlagString = toFlagString;
  function messageAndStackFor(error2, parentStack) {
    let message2;
    let rawStack;
    let id;
    let path;
    if (error2 !== void 0 && error2 !== null) {
      if (typeof error2 === "string" || typeof error2 === "number") {
        return { message: `${error2}` };
      }
      ({ message: message2, stack: rawStack, id, path } = error2);
      if (message2 === void 0) {
        message2 = error2.toString();
      }
      if (typeof id !== "string") {
        id = void 0;
      }
    }
    if (message2 === void 0 || message2 === null || message2 === "") {
      if (error2 !== void 0 && error2 !== null) {
        message2 = error2.constructor.name;
        if (!message2 || message2 === "Error") {
          message2 = "(unknown error)";
        }
      } else {
        message2 = "(unknown error)";
      }
    }
    if (!rawStack) {
      return { message: message2, id, path };
    }
    rawStack = rawStack.toString();
    rawStack = rawStack.replace(/^.*?\n\nError: /gs, "Error: ");
    if (rawStack.startsWith("Error: ")) {
      rawStack = rawStack.slice(7);
    }
    const pos = rawStack.indexOf(message2);
    if (pos !== -1) {
      rawStack = rawStack.slice(pos + message2.length).trim();
    }
    let stackLines = rawStack.split("\n").map((line) => line.trim()).filter((line) => line !== "");
    if (stackLines[0] === "Error") {
      stackLines.shift();
    }
    let truncatedToParent = false;
    if (parentStack) {
      let truncateTo = 0;
      stackSearch: for (; truncateTo < stackLines.length - 1; truncateTo++) {
        let parentPos = parentStack.indexOf(stackLines[truncateTo]);
        if (parentPos === -1) {
          continue;
        }
        parentPos++;
        for (let pos2 = truncateTo + 1; pos2 < stackLines.length && parentPos < parentStack.length; pos2++, parentPos++) {
          if (stackLines[pos2] !== parentStack[parentPos]) {
            continue stackSearch;
          }
        }
        stackLines = stackLines.slice(0, truncateTo + 1);
        truncatedToParent = true;
        break;
      }
    }
    const stack = Array();
    for (const line of stackLines) {
      const match1 = line.match(/^at\s+(?:(\S|\S.*\S)\s+\(([^)]+)\)|(<anonymous>))$/);
      if (match1) {
        const value2 = [Diagnostic2.weak("at "), match1[1] ?? match1[3]];
        if (match1[2] !== void 0) {
          value2.push(Diagnostic2.weak(" ("), Diagnostic2.weak(match1[2]), Diagnostic2.weak(")"));
        }
        stack.push(Diagnostic2.squash(...value2));
        continue;
      }
      const match2 = line.match(/^at\s+(\S.*)(:\d+:\d+)$/);
      if (match2) {
        stack.push(Diagnostic2.squash(Diagnostic2.weak("at "), match2[1], Diagnostic2.weak(match2[2])));
        continue;
      }
      stack.push(line);
    }
    if (truncatedToParent) {
      stack.push(Diagnostic2.weak("(see parent frames)"));
    }
    return { message: message2, id, path, stack, stackLines };
  }
  Diagnostic2.messageAndStackFor = messageAndStackFor;
})(Diagnostic || (Diagnostic = {}));
function formatError(error, options = {}) {
  const { messagePrefix, parentStack } = options;
  const messageAndStack = Diagnostic.messageAndStackFor(error, parentStack);
  let { stack, stackLines } = messageAndStack;
  let { message } = messageAndStack;
  const messageDiagnostic = Array();
  if (messagePrefix) {
    messageDiagnostic.push(messagePrefix);
  }
  messageDiagnostic.push(Diagnostic.errorMessage({ ...messageAndStack, cause: void 0 }));
  message = Diagnostic.upgrade(message, messageDiagnostic);
  let cause, errors, secondary;
  if (typeof error === "object" && error !== null) {
    if ("error" in error && "suppressed" in error) {
      secondary = error.error;
      error = error.suppressed;
    }
    ({ cause, errors } = error);
  }
  if (error && errorCollector?.(error)) {
    stack = stackLines = void 0;
  }
  if (stack === void 0 && cause === void 0 && errors === void 0) {
    return message;
  }
  const list = [message];
  if (stack === void 0) {
    list.push(Diagnostic("list", []));
  } else {
    list.push(Diagnostic("list", stack));
  }
  for (; typeof cause === "object" && cause !== null; cause = cause.cause) {
    let formatted = formatError(cause, { messagePrefix: "Caused by:", parentStack: stackLines });
    if (Diagnostic.presentationOf(formatted) === "list") {
      formatted = Diagnostic.valueOf(formatted) ?? formatted;
    }
    if (Array.isArray(formatted)) {
      list.push(...formatted);
    } else {
      list.push(formatted);
    }
  }
  if (Array.isArray(errors)) {
    let cause2 = 0;
    list.push(
      Diagnostic.list(
        errors.map((e) => formatError(e, { messagePrefix: `Cause #${cause2++}:`, parentStack: stackLines }))
      )
    );
  }
  if (secondary) {
    list.push(Diagnostic.list([formatError(secondary, { messagePrefix: "Secondary error during disposal:" })]));
  }
  return list;
}
export {
  Diagnostic
};
//# sourceMappingURL=Diagnostic.js.map
