"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var VariableService_exports = {};
__export(VariableService_exports, {
  VariableService: () => VariableService
});
module.exports = __toCommonJS(VariableService_exports);
var import_MatterError = require("../MatterError.js");
var import_Diagnostic = require("../log/Diagnostic.js");
var import_Type = require("../util/Type.js");
var import_Environmental = require("./Environmental.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class VariableService {
  #vars = {};
  #usageCollectors = Array();
  #usages = /* @__PURE__ */ new Set();
  constructor(environment) {
    environment.set(VariableService, this);
  }
  static [import_Environmental.Environmental.create](environment) {
    return new this(environment);
  }
  get [import_Diagnostic.Diagnostic.value]() {
    return "Configuration";
  }
  get vars() {
    return this.#vars;
  }
  /**
   * Configure a component.
   *
   * Runs the supplied {@link configurator} immediately and whenever variables reference by the {@link configurator}
   * change.
   *
   * To terminate reconfiguration invoke {@link VariableService.Usage.close} on the return value.
   */
  use(configurator) {
    const variables = /* @__PURE__ */ new Set();
    try {
      this.#usageCollectors.push(variables);
      configurator();
    } finally {
      this.#usageCollectors.pop();
    }
    const usage = {
      variables,
      configurator,
      close: () => {
        this.#usages.delete(usage);
      }
    };
    this.#usages.add(usage);
    return usage;
  }
  /**
   * Get a variable value.
   * The name is first checked as defined, if no value is found and the name contains non-alphanumeric characters,
   * the name is parsed with all "non-environment-var chars" replaced by dots and checked again.
   * If the name is still not found, the {@link fallback} value is returned.
   */
  get(name, fallback) {
    for (const collector of this.#usageCollectors) {
      collector.add(name);
    }
    switch (typeof fallback) {
      case "string":
        return this.string(name) ?? fallback;
      case "number":
        return this.number(name) ?? fallback;
      case "boolean":
        return this.boolean(name) ?? fallback;
    }
    let value = this.#maybeValue(name);
    if (value === void 0 && name.match(/[^A-Z0-9.]/gi)) {
      const sanitizedName = name.split(/[^A-Z0-9]+/i).filter(Boolean).join(".");
      if (sanitizedName !== "") {
        value = this.#maybeValue(sanitizedName);
        if (value !== void 0) {
          for (const collector of this.#usageCollectors) {
            collector.add(sanitizedName);
          }
        }
      }
    }
    return value ?? fallback;
  }
  #maybeValue(name) {
    let value = this.#vars;
    for (const segment of this.#parseName(name)) {
      if (value === null || typeof value !== "object" || Array.isArray(value)) {
        return void 0;
      }
      value = value[segment];
    }
    return value ?? void 0;
  }
  has(name) {
    return this.get(name) !== void 0;
  }
  set(name, value) {
    const segments = name.toLowerCase().split(".");
    const key = segments.pop();
    if (key === "__proto__" || key === "constructor" || key === "prototype") {
      throw new import_MatterError.ImplementationError(`Invalid variable path name: ${key}`);
    }
    let parent = this.#vars;
    for (const segment of segments) {
      let nextParent = parent[segment];
      if (nextParent === void 0 || nextParent === null) {
        nextParent = parent[segment] = {};
      } else if (typeof nextParent !== "object" || Array.isArray(nextParent)) {
        throw new import_MatterError.ImplementationError(`Can't set ${name} because segment ${segment} is not a map`);
      }
      parent = nextParent;
    }
    if (parent[key] === value) {
      return;
    }
    parent[key] = value;
    for (const usage of this.#usages) {
      if (usage.variables.has(name)) {
        usage.configurator();
      }
    }
  }
  async persist(name, value) {
    this.set(name, value);
    await this.persistConfigValue?.(name, value);
  }
  persistConfigValue;
  string(name) {
    const value = this.get(name);
    if (value === void 0) {
      return value;
    }
    if (typeof value === "string") {
      return value;
    }
    if (value === null) {
      return "null";
    }
    return value.toString();
  }
  boolean(name) {
    const value = this.get(name);
    switch (value) {
      case void 0:
        return value;
      case null:
      case 0:
      case "0":
      case false:
      case "false":
      case "off":
        return false;
      default:
        return true;
    }
  }
  number(name) {
    let value = this.get(name);
    if (typeof value === "number") {
      return value;
    }
    if (typeof value === "string") {
      value = Number.parseFloat(value);
      if (!Number.isFinite(value)) {
        return;
      }
      return value;
    }
  }
  integer(name, fallback) {
    const number = this.number(name) ?? fallback;
    if (typeof number === "number") {
      return Math.floor(number);
    }
  }
  increment(name) {
    const value = this.integer(name) ?? 0;
    this.set(name, value + 1);
    return value;
  }
  addConfigStyle(vars) {
    const path = Array();
    const add = (vars2) => {
      for (const name in vars2) {
        const val = vars2[name];
        if ((0, import_Type.isObject)(val)) {
          path.push(name);
          add(val);
          path.pop();
        } else {
          this.set([...path, name].join("."), val);
        }
      }
    };
    add(vars);
  }
  addUnixEnvStyle(vars) {
    this.addConfigStyle(parseUnixStyle(vars));
  }
  addArgvStyle(vars) {
    this.addConfigStyle(parseArgvStyle(vars));
  }
  #parseName(name) {
    return name.toLowerCase().split(".").map((segment) => {
      segment = segment.trim();
      if (segment === "") {
        throw new import_MatterError.ImplementationError(`Variable name ${name} contains empty segments`);
      }
      return segment;
    });
  }
}
function addVariable(into, path, value) {
  if (!path.length) {
    return;
  }
  const firstPathEntry = path[0];
  if (firstPathEntry === "__proto__" || firstPathEntry === "constructor" || firstPathEntry === "prototype") {
    throw new import_MatterError.ImplementationError(`Invalid variable path name: ${firstPathEntry}`);
  }
  let current = into[firstPathEntry];
  if (path.length === 1) {
    if (current === void 0) {
      into[firstPathEntry] = value;
    }
    return;
  }
  if (typeof current !== "object") {
    current = into[firstPathEntry] = {};
  }
  addVariable(current, path.slice(1), value);
}
function parseUnixStyle(values) {
  const variables = {};
  for (const key in values) {
    if (key.startsWith("MATTER_")) {
      if (values[key] === void 0 || values[key] === "") {
        continue;
      }
      addVariable(variables, key.slice(7).toLowerCase().split("_"), values[key]);
    }
  }
  return variables;
}
function parseArgvStyle(values) {
  const variables = {};
  for (let arg of values) {
    if (!arg.startsWith("--")) {
      continue;
    }
    arg = arg.slice(2);
    const separatorPos = arg.indexOf("=");
    let key, value;
    if (separatorPos === -1) {
      key = arg;
      value = true;
    } else {
      key = arg.slice(0, separatorPos);
      value = arg.slice(separatorPos + 1);
      if (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"')) {
        value = value.slice(1, -1).replaceAll("\\'", "'").replaceAll('\\"', '"');
      }
    }
    addVariable(variables, key.toLowerCase().split("-"), value);
  }
  return variables;
}
//# sourceMappingURL=VariableService.js.map
