/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Boot } from "#util/Boot.js";
import { CancelablePromise } from "#util/Cancelable.js";
import { ImplementationError, NotImplementedError } from "../MatterError.js";
import { Time } from "../time/Time.js";
import { Bytes } from "../util/Bytes.js";
import { Diagnostic } from "./Diagnostic.js";
import { LogDestination, LogDestinations } from "./LogDestination.js";
import { LogFormat } from "./LogFormat.js";
import { LogLevel } from "./LogLevel.js";
class Logger {
  /**
   * Log destinations.
   *
   * By default there is a single destination named "default".  You can create new destinations using
   * {@link LogDestination}.  Add or remove destinations by modifying this object.
   *
   * Throws an error if you access a destination that doesn't exist.
   */
  static destinations = LogDestinations();
  /**
   * The number of indents to print with messages.
   */
  static nestingLevel;
  #name;
  /**
   * Create a new logger for a facility.
   *
   * @param name the name of the facility
   * @returns a new facility
   */
  static get(name) {
    return new Logger(name);
  }
  /**
   * Get the default log level.
   */
  static get level() {
    return LogDestination.defaults.level;
  }
  /**
   * Set log level as name or number for all destinations.
   */
  static set level(level) {
    level = LogLevel(level);
    LogDestination.defaults.level = level;
    for (const name in this.destinations) {
      this.destinations[name].level = level;
    }
  }
  /**
   * Get the default facility levels.
   */
  static get facilityLevels() {
    return LogDestination.defaults.facilityLevels;
  }
  /**
   * Set log level as name or number for facilities in all destinations.
   *
   * Existing levels that are not named in {@link levels} will remain unchanged.
   */
  static set facilityLevels(levels) {
    for (const name in levels) {
      levels[name] = LogLevel(levels[name]);
    }
    Object.assign(LogDestination.defaults.facilityLevels, levels);
    for (const name in this.destinations) {
      Object.assign(this.destinations[name].facilityLevels, levels);
    }
  }
  /**
   * Get the default format name.
   */
  static get format() {
    return LogDestination.defaults.format.name;
  }
  /**
   * Set the format for all destinations.
   */
  static set format(format) {
    format = LogFormat(format);
    LogDestination.defaults.format = format;
    for (const name in this.destinations) {
      this.destinations[name].format = format;
    }
  }
  /**
   * Mask a string with a given character. If unmaskedLength is provided then these number of characters will be
   * shown unmasked.
   *
   * @param str String to mask
   * @param maskChar character to mask with
   * @param unmaskedLength number of characters to show unmasked in the beginning
   */
  static maskString(str, maskChar = "*", unmaskedLength) {
    return str.substring(0, unmaskedLength ?? 0) + str.substring(unmaskedLength ?? 0).replace(/./g, maskChar);
  }
  /**
   * Perform operations in a nested logging context.  Messages will be
   * indented while the context executes.
   */
  static nest(context) {
    this.nestingLevel++;
    try {
      return context();
    } finally {
      this.nestingLevel--;
    }
  }
  /**
   * Async version of nest().
   */
  static async nestAsync(context) {
    this.nestingLevel++;
    try {
      return await context();
    } finally {
      this.nestingLevel--;
    }
  }
  /**
   * Unhandled error reporter.
   *
   * Some environments do not report full error details such as {@link Error#cause} and {@link AggregateError#errors}.
   *
   * To ensure these details are always recorded somewhere, unhandled errors may be reported here.
   *
   * To disable this behavior replace this function.
   */
  static reportUnhandledError(error) {
    try {
      Logger.get("Unhandled").fatal("Unhandled error detected:", error);
    } catch (e) {
    }
  }
  constructor(name) {
    this.#name = name;
  }
  debug(...values) {
    this.#log(LogLevel.DEBUG, values);
  }
  info(...values) {
    this.#log(LogLevel.INFO, values);
  }
  notice(...values) {
    this.#log(LogLevel.NOTICE, values);
  }
  warn(...values) {
    this.#log(LogLevel.WARN, values);
  }
  error(...values) {
    this.#log(LogLevel.ERROR, values);
  }
  fatal(...values) {
    this.#log(LogLevel.FATAL, values);
  }
  log(level, ...values) {
    this.#log(level, values);
  }
  #log(level, values) {
    for (const name in Logger.destinations) {
      const dest = Logger.destinations[name];
      if (level < (dest.facilityLevels?.[this.#name] ?? dest.level)) {
        continue;
      }
      if (!dest.context) {
        dest.context = Diagnostic.Context();
      }
      dest.context.run(
        () => dest.add(
          Diagnostic.message({
            now: Time.now,
            facility: this.#name,
            level,
            prefix: nestingPrefix(),
            values
          })
        )
      );
    }
  }
  //
  // DEPRECATED API SURFACE FOLLOWS
  //
  /**
   * Stringify a value (BigInt aware) as JSON.
   *
   * @param data the value to stringify
   * @returns the stringified value
   *
   * @deprecated use {@link Diagnostic.json}
   */
  static toJSON(data) {
    return JSON.stringify(data, (_, value) => {
      if (typeof value === "bigint") {
        return value.toString();
      }
      if (Bytes.isBytes(value)) {
        return Bytes.toHex(value);
      }
      if (value === void 0) {
        return "undefined";
      }
      return value;
    });
  }
  /**
   * Add additional logger to the list of loggers including the default configuration.
   *
   * @deprecated use {@link destinations}
   */
  static addLogger(identifier, logger, options) {
    if (identifier in this.destinations) {
      throw new ImplementationError(`Logger "${identifier}" already exists`);
    }
    const dest = LogDestination({ name: identifier });
    const legacy = adaptDestinationToLegacy(dest);
    legacy.log = logger;
    if (options?.defaultLogLevel !== void 0) {
      legacy.defaultLogLevel = options.defaultLogLevel;
    }
    if (options?.logLevels !== void 0) {
      legacy.logLevels = options.logLevels;
    }
    if (options?.logFormat !== void 0) {
      legacy.logFormatter = logFormatterFor(options.logFormat);
    }
    this.destinations[identifier] = dest;
  }
  /**
   * @deprecated use {@link destinations}
   */
  static removeLogger(identifier) {
    if (!(identifier in this.destinations)) {
      throw new NotImplementedError(`Logger "${identifier}" does not exist`);
    }
    delete this.destinations[identifier];
  }
  /**
   * Check if a logger with the matching identifier exists.
   * @param identifier The identifier of the logger
   *
   * @deprecated use {@link destinations}
   */
  static hasLoggerForIdentifier(identifier) {
    return identifier in this.destinations;
  }
  /**
   * Get the logger with the matching identifier.
   * @param identifier The identifier of the logger
   *
   * @deprecated use {@link destinations}
   */
  static getLoggerForIdentifier(identifier) {
    const dest = this.destinations[identifier];
    if (dest === void 0) {
      throw new NotImplementedError(`Unknown logger "${identifier}"`);
    }
    return adaptDestinationToLegacy(dest);
  }
  /**
   * @deprecated use {@link destinations}
   */
  static getLoggerforIdentifier(identifier) {
    return this.getLoggerForIdentifier(identifier);
  }
  /**
   * Set facility loglevels for the default logger.
   * @param levels The levels to set
   *
   * @deprecated use {@link destinations}
   */
  static set logLevels(levels) {
    Logger.setLogLevelsForLogger("default", levels);
  }
  /**
   * Get facility loglevels for the default logger.
   *
   * @deprecated use {@link Logger.facilityLevels}
   */
  static get logLevels() {
    return Logger.getLoggerForIdentifier("default").logLevels;
  }
  /**
   * Set default loglevel for the default logger.
   *
   * @param level The level to set
   *
   * @deprecated use {@link Logger.level}
   */
  static set defaultLogLevel(level) {
    Logger.setDefaultLoglevelForLogger("default", level);
  }
  /**
   * Get default loglevel for the default logger.
   *
   * @deprecated use {@link destinations}
   */
  static get defaultLogLevel() {
    return Logger.getLoggerForIdentifier("default").defaultLogLevel;
  }
  /**
   * Set the log function for the default logger.
   *
   * @param log The log function to set
   *
   * @deprecated use {@link destinations}
   */
  static set log(log) {
    Logger.setLogger("default", log);
  }
  /**
   * Get the log function for the default logger.
   *
   * @deprecated use {@link destinations}
   */
  static get log() {
    return Logger.getLoggerForIdentifier("default").log;
  }
  /**
   * Set the log formatter for the default logger.
   *
   * @param logFormatter
   *
   * @deprecated use {@link destinations}
   */
  static set logFormatter(logFormatter) {
    Logger.setLogFormatterForLogger("default", logFormatter);
  }
  /**
   * Get the log formatter for the default logger.
   *
   * @deprecated use {@link destinations}
   */
  static get logFormatter() {
    return Logger.getLoggerForIdentifier("default").logFormatter;
  }
  /**
   * Set logFormatter using configuration-style format name for the logger with the matching identifier.
   *
   * @param identifier The identifier of the logger
   * @param format the name of the formatter (see Format enum)
   *
   * @deprecated use {@link destinations}
   */
  static setFormatForLogger(identifier, format) {
    this.getLoggerForIdentifier(identifier).logFormatter = logFormatterFor(format);
  }
  /**
   * Set default loglevel for the logger with the matching identifier.
   *
   * @param identifier The identifier of the logger
   * @param level The level to set
   *
   * @deprecated use {@link destinations}
   */
  static setDefaultLoglevelForLogger(identifier, level) {
    this.getLoggerForIdentifier(identifier).defaultLogLevel = level;
  }
  /**
   * Set facility loglevels for the logger with the matching identifier.
   *
   * @param identifier The identifier of the logger
   * @param levels The levels to set
   *
   * @deprecated use {@link destinations}
   */
  static setLogLevelsForLogger(identifier, levels) {
    this.getLoggerForIdentifier(identifier).logLevels = levels;
  }
  /**
   * Set the log function for the logger with the matching identifier.
   *
   * @param identifier The identifier of the logger
   * @param log The log function to set
   *
   * @deprecated use {@link destinations}
   */
  static setLogger(identifier, log) {
    this.getLoggerForIdentifier(identifier).log = log;
  }
  /**
   * Set the log formatter for the logger with the matching identifier.
   *
   * @param identifier The identifier of the logger
   * @param logFormatter The log formatter to set
   *
   * @deprecated use {@link destinations}
   */
  static setLogFormatterForLogger(identifier, logFormatter) {
    this.getLoggerForIdentifier(identifier).logFormatter = logFormatter;
  }
}
function nestingPrefix() {
  if (Logger.nestingLevel) {
    return "\u23B8".padEnd(Logger.nestingLevel * 2);
  }
  return "";
}
Boot.init(() => {
  Logger.destinations = LogDestinations();
  Logger.nestingLevel = 0;
  if (typeof MatterHooks !== "undefined") {
    MatterHooks.loggerSetup?.(Logger);
  }
});
CancelablePromise.logger = Logger.get("CancelablePromise");
function logFormatterFor(formatName) {
  const format = LogFormat(formatName);
  return (now, level, facility, prefix, ...values) => format(Diagnostic.message({ now, level, facility, prefix, values }));
}
function adaptDestinationToLegacy(destination) {
  return {
    get logIdentifier() {
      return destination.name;
    },
    get logFormatter() {
      return (now, level, facility, prefix, values) => destination.format(Diagnostic.message({ now, level, facility, prefix, values }));
    },
    set logFormatter(logFormatter) {
      destination.format = (message) => logFormatter(message.now, message.level, message.facility, message.prefix, message.values);
    },
    get log() {
      return (level, formattedLog, facility) => destination.write(formattedLog, Diagnostic.message({ level, facility }));
    },
    set log(log) {
      destination.write = (text, message) => log(message.level, text, message.facility);
    },
    get defaultLogLevel() {
      return destination.level;
    },
    set defaultLogLevel(level) {
      destination.level = level;
    },
    get logLevels() {
      return destination.facilityLevels;
    },
    set logLevels(levels) {
      destination.facilityLevels = levels;
    }
  };
}
export {
  Logger
};
//# sourceMappingURL=Logger.js.map
