var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { NodeActivity } from "#behavior/context/NodeActivity.js";
import { IndexBehavior } from "#behavior/system/index/IndexBehavior.js";
import { NetworkBehavior } from "#behavior/system/network/NetworkBehavior.js";
import { NetworkRuntime } from "#behavior/system/network/NetworkRuntime.js";
import { PartsBehavior } from "#behavior/system/parts/PartsBehavior.js";
import { Endpoint } from "#endpoint/Endpoint.js";
import { Endpoints } from "#endpoint/properties/Endpoints.js";
import { MutableEndpoint } from "#endpoint/type/MutableEndpoint.js";
import {
  asError,
  Construction,
  Diagnostic,
  DiagnosticPresentation,
  DiagnosticSource,
  Environment,
  ImplementationError,
  Logger
} from "@matter/general";
import { RootEndpoint } from "../endpoints/root.js";
import { NodeLifecycle } from "./NodeLifecycle.js";
import { ProtocolService } from "./integration/ProtocolService.js";
const logger = Logger.get("Node");
class Node extends Endpoint {
  #environment;
  #runtime;
  #startInProgress = false;
  #closeInProgress = false;
  constructor(config) {
    const parentEnvironment = config.environment ?? config.owner?.env ?? Environment.default;
    void parentEnvironment.root.runtime;
    if (config.id === void 0) {
      config.id = `node${parentEnvironment.vars.increment("node.nextFallbackId")}`;
    }
    super(config);
    this.#environment = new Environment(config.id, parentEnvironment);
    this.#environment.set(NodeActivity, new NodeActivity());
    this.#environment.set(ProtocolService, new ProtocolService(this));
    if (this.lifecycle.hasNumber) {
      if (this.number !== 0) {
        throw new ImplementationError("The root endpoint ID must be 0");
      }
    } else {
      this.number = 0;
    }
    this.lifecycle.online.on(() => {
      this.statusUpdate("is online");
    });
    this.lifecycle.offline.on(() => {
      this.statusUpdate("is offline");
    });
    this.lifecycle.goingOffline.on(() => {
      this.statusUpdate("going offline");
    });
  }
  get env() {
    return this.#environment;
  }
  /**
   * The optimized view that supports local Matter protocol implementation.
   */
  get protocol() {
    return this.env.get(ProtocolService).protocol;
  }
  get lifecycle() {
    return super.lifecycle;
  }
  /**
   * Bring the node online.
   */
  async start() {
    if (this.lifecycle.isOnline || this.#startInProgress) {
      return;
    }
    this.#startInProgress = true;
    try {
      this.lifecycle.targetState = "online";
      await this.lifecycle.mutex.produce(this.startWithMutex.bind(this));
    } catch (error) {
      this.lifecycle.targetState = "offline";
      throw error;
    } finally {
      this.#startInProgress = false;
    }
  }
  async startWithMutex() {
    this.env.runtime.add(this);
    try {
      await this.construction.ready;
      if (this.#runtime) {
        return;
      }
      this.statusUpdate("going online");
      this.#runtime = this.createRuntime();
      this.#runtime.construction.start();
      this.#environment.set(NetworkRuntime, this.#runtime);
      await this.#runtime.construction.ready;
      await this.act("network startup", (agent) => agent.get(NetworkBehavior).startup());
    } catch (e) {
      if (this.#runtime) {
        const runtime = this.#runtime;
        this.#environment.delete(NetworkRuntime, runtime);
        this.#runtime = void 0;
        this.behaviors.internalsOf(NetworkBehavior).runtime = void 0;
        runtime.close().catch((e2) => {
          logger.warn("Error closing network runtime", asError(e2));
        });
      }
      this.env.runtime.delete(this);
      throw e;
    }
  }
  /**
   * @deprecated use {@link start}
   */
  async bringOnline() {
    return this.start();
  }
  /**
   * Run the node in standalone mode.  Returns when the node is closed.
   */
  async run() {
    await this.start();
    await this.construction.closed;
  }
  /**
   * Take the node offline but leave state and structure intact.  Happens automatically on close.
   *
   * Once the node is offline you may use {@link start} to bring the node online again.
   */
  async stop() {
    this.lifecycle.targetState = "offline";
    await this.lifecycle.mutex.produce(this.cancelWithMutex.bind(this));
  }
  /**
   * @deprecated use {@link stop}
   */
  cancel() {
    return this.stop();
  }
  /**
   * All endpoints owned by the node.
   *
   * Normally you access endpoints via {@link parts} but you can use this property access endpoints directly by
   * {@link EndpointNumber}.
   */
  get endpoints() {
    return new Endpoints(this);
  }
  async cancelWithMutex() {
    if (!this.#runtime) {
      return;
    }
    await this.act((agent) => this.lifecycle.goingOffline.emit(agent.context));
    if (this.#runtime) {
      this.#environment.delete(NetworkRuntime, this.#runtime);
    }
    await this.#runtime?.close();
    this.#runtime = void 0;
  }
  async close() {
    if (this.#closeInProgress) {
      return;
    }
    this.#closeInProgress = true;
    try {
      this.lifecycle.targetState = "offline";
      await this.lifecycle.mutex.close();
      await this.closeWithMutex();
    } finally {
      this.#closeInProgress = false;
    }
  }
  async closeWithMutex() {
    if (this.#runtime) {
      await this.cancelWithMutex();
    }
    await super.close();
  }
  async reset() {
    await this.lifecycle.mutex.produce(this.resetWithMutex.bind(this));
  }
  async resetWithMutex() {
    return super.reset();
  }
  get [DiagnosticPresentation.name]() {
    return ["Runtime for", Diagnostic.strong(this.toString())];
  }
  get [Diagnostic.value]() {
    var _stack = [];
    try {
      const nodeActivity = this.#environment.get(NodeActivity);
      const _activity = __using(_stack, nodeActivity.begin("diagnostics"));
      return Diagnostic.node("\u{1F9E9}", this.id, {
        children: [
          Diagnostic.strong("Structure"),
          Diagnostic.list([super[Diagnostic.value]]),
          Diagnostic.strong("Activity"),
          nodeActivity[Diagnostic.value]
        ]
      });
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  createLifecycle() {
    return new NodeLifecycle(this);
  }
  statusUpdate(message) {
    logger.notice(Diagnostic.strong(this.toString()), message);
  }
  async [Construction.destruct]() {
    await this.cancelWithMutex();
    await super[Construction.destruct]();
    DiagnosticSource.delete(this);
    this.#environment[Symbol.dispose]();
  }
}
((Node2) => {
  function nodeConfigFor(defaultType, configuration, options) {
    if (configuration === void 0) {
      return {
        type: defaultType,
        ...options
      };
    }
    if (configuration.deviceType !== void 0) {
      return {
        type: configuration,
        ...options
      };
    }
    return {
      type: defaultType,
      ...configuration
    };
  }
  Node2.nodeConfigFor = nodeConfigFor;
  function forEndpoint(endpoint) {
    const node = endpoint.ownerOfType(RootEndpoint);
    if (node === void 0) {
      throw new ImplementationError(`Cannot complete operation because ${endpoint} is not installed in a node`);
    }
    if (!(node instanceof Node2)) {
      throw new ImplementationError(`Root endpoint for ${endpoint} is not a node`);
    }
    return node;
  }
  Node2.forEndpoint = forEndpoint;
  Node2.CommonRootEndpoint = MutableEndpoint({
    name: RootEndpoint.name,
    deviceType: RootEndpoint.deviceType,
    deviceRevision: RootEndpoint.deviceRevision,
    deviceClass: RootEndpoint.deviceClass,
    requirements: RootEndpoint.requirements,
    behaviors: {
      parts: PartsBehavior,
      index: IndexBehavior
    }
  });
})(Node || (Node = {}));
export {
  Node
};
//# sourceMappingURL=Node.js.map
