/**
 * @license
 * Copyright 2025-2026 Open Home Foundation
 * SPDX-License-Identifier: Apache-2.0
 */
import { NodeStates } from "@project-chip/matter.js/device";
import { ServerError } from "../types/WebSocketMessageTypes.js";
import { AttributeDataCache } from "./AttributeDataCache.js";
class Nodes {
  #nodes = /* @__PURE__ */ new Map();
  #attributeCache = new AttributeDataCache();
  /** Cached so serialization and event paths always agree on availability. */
  #lastAvailability = /* @__PURE__ */ new Map();
  /**
   * Buffered endpoint_added events. python-matter-server wire contract requires
   * node_updated (carrying the new endpoint) to arrive before endpoint_added,
   * so HA can resolve node.endpoints[endpoint_id] in its callback.
   */
  #pendingEndpointAdds = /* @__PURE__ */ new Map();
  get attributeCache() {
    return this.#attributeCache;
  }
  getIds() {
    return Array.from(this.#nodes.keys());
  }
  /** @throws ServerError if node not found */
  get(nodeId) {
    const node = this.#nodes.get(nodeId);
    if (node === void 0) {
      throw ServerError.nodeNotExists(nodeId);
    }
    return node;
  }
  has(nodeId) {
    return this.#nodes.has(nodeId);
  }
  set(nodeId, node) {
    this.#nodes.set(nodeId, node);
  }
  delete(nodeId) {
    this.#nodes.delete(nodeId);
    this.#attributeCache.delete(nodeId);
    this.#lastAvailability.delete(nodeId);
    this.#pendingEndpointAdds.delete(nodeId);
  }
  queueEndpointAdded(nodeId, endpointId) {
    let queue = this.#pendingEndpointAdds.get(nodeId);
    if (queue === void 0) {
      queue = [];
      this.#pendingEndpointAdds.set(nodeId, queue);
    }
    queue.push(endpointId);
  }
  /** Returns insertion-ordered queue; empty if nothing pending. */
  drainPendingEndpointAdds(nodeId) {
    const queue = this.#pendingEndpointAdds.get(nodeId);
    if (queue === void 0 || queue.length === 0) {
      return [];
    }
    this.#pendingEndpointAdds.delete(nodeId);
    return queue;
  }
  seedState(nodeId, initialState) {
    this.#lastAvailability.set(nodeId, initialState === NodeStates.Connected);
  }
  /** `debouncePending` = reconnect timer armed by caller; keeps non-Connected states available. */
  processStateChange(nodeId, newState, debouncePending) {
    const wasAvailable = this.#lastAvailability.get(nodeId) ?? false;
    const available = this.isNodeAvailable(newState, debouncePending);
    this.#lastAvailability.set(nodeId, available);
    if (wasAvailable !== available) {
      return { availabilityChanged: true, available };
    }
    return { availabilityChanged: false };
  }
  /** Returns true if the node was previously considered available. */
  forceUnavailable(nodeId) {
    const wasAvailable = this.#lastAvailability.get(nodeId) ?? false;
    this.#lastAvailability.set(nodeId, false);
    return wasAvailable;
  }
  isNodeAvailable(currentState, debouncePending = false) {
    if (currentState === NodeStates.Connected) {
      return true;
    }
    return debouncePending;
  }
  /** Returns the cached value, not a recomputation — avoids disagreement with the event path. */
  isAvailable(nodeId) {
    return this.#lastAvailability.get(nodeId) ?? false;
  }
}
export {
  Nodes
};
//# sourceMappingURL=Nodes.js.map
