/**
 * @license
 * Copyright 2025-2026 Open Home Foundation
 * SPDX-License-Identifier: Apache-2.0
 */
import { Logger, NodeId, Observable } from "@matter/main";
import { parseBigIntAwareJson, splitAttributePath } from "../server/Converters.js";
import { ServerError, TEST_NODE_START } from "../types/WebSocketMessageTypes.js";
const logger = Logger.get("TestNodeCommandHandler");
class TestNodeCommandHandler {
  #testNodes = /* @__PURE__ */ new Map();
  /** Observable for node added events */
  nodeAdded = new Observable();
  /** Observable for node removed events */
  nodeRemoved = new Observable();
  /**
   * Check if a node ID is in the test node range (>= TEST_NODE_START).
   */
  static isTestNodeId(nodeId) {
    const bigId = typeof nodeId === "bigint" ? nodeId : BigInt(nodeId);
    return bigId >= TEST_NODE_START;
  }
  /**
   * Check if this handler manages the given node ID.
   */
  hasNode(nodeId) {
    return this.#testNodes.has(BigInt(nodeId));
  }
  /**
   * Get all test node IDs.
   */
  getNodeIds() {
    return Array.from(this.#testNodes.keys()).map((id) => NodeId(id));
  }
  /**
   * Get all test nodes.
   */
  getNodes() {
    return Array.from(this.#testNodes.values());
  }
  /**
   * Get a test node by ID.
   */
  getNode(nodeId) {
    return this.#testNodes.get(BigInt(nodeId));
  }
  /**
   * Get full node details in WebSocket API format.
   */
  async getNodeDetails(nodeId) {
    const testNode = this.#testNodes.get(BigInt(nodeId));
    if (testNode === void 0) {
      throw ServerError.nodeNotExists(nodeId);
    }
    return testNode;
  }
  /**
   * Read multiple attributes from a test node by path strings.
   * Handles wildcards in paths.
   */
  async handleReadAttributes(nodeId, attributePaths, _fabricFiltered) {
    const testNode = this.#testNodes.get(BigInt(nodeId));
    if (testNode === void 0) {
      throw ServerError.nodeNotExists(nodeId);
    }
    const result = {};
    for (const path of attributePaths) {
      const { endpointId, clusterId, attributeId } = splitAttributePath(path);
      if (path.includes("*")) {
        for (const [attrPath, value] of Object.entries(testNode.attributes)) {
          const parts = attrPath.split("/").map(Number);
          if ((endpointId === void 0 || parts[0] === endpointId) && (clusterId === void 0 || parts[1] === clusterId) && (attributeId === void 0 || parts[2] === attributeId)) {
            result[attrPath] = value;
          }
        }
      } else {
        result[path] = testNode.attributes[path];
      }
    }
    return result;
  }
  /**
   * Import test nodes from a diagnostic dump.
   * @param dump JSON string containing the diagnostic dump
   * @returns Array of imported node IDs
   */
  importTestNodes(dump) {
    const dumpData = parseBigIntAwareJson(dump);
    let dumpNodes;
    if (dumpData?.data?.node) {
      dumpNodes = [dumpData.data.node];
    } else if (dumpData?.data?.server?.nodes) {
      dumpNodes = Object.values(dumpData.data.server.nodes);
    } else if (dumpData?.data?.nodes) {
      dumpNodes = Object.values(dumpData.data.nodes);
    } else {
      throw ServerError.invalidArguments("Invalid dump format: cannot find node data");
    }
    let nextTestNodeId = TEST_NODE_START;
    for (const existingId of this.#testNodes.keys()) {
      if (existingId >= nextTestNodeId) {
        nextTestNodeId = existingId + 1n;
      }
    }
    const importedNodeIds = [];
    for (const nodeDict of dumpNodes) {
      const testNodeId = nextTestNodeId++;
      const nodeId = NodeId(testNodeId);
      const testNode = {
        node_id: testNodeId,
        date_commissioned: nodeDict.date_commissioned,
        last_interview: nodeDict.last_interview,
        interview_version: nodeDict.interview_version,
        available: nodeDict.available,
        is_bridge: nodeDict.is_bridge,
        attributes: nodeDict.attributes,
        attribute_subscriptions: []
      };
      this.#testNodes.set(testNodeId, testNode);
      importedNodeIds.push(nodeId);
      logger.info(`Imported test node ${testNodeId} with ${Object.keys(testNode.attributes).length} attributes`);
      this.nodeAdded.emit(nodeId, testNode);
    }
    return importedNodeIds;
  }
  /**
   * Write an attribute to a test node.
   * Logs the write and returns success (no actual write occurs).
   */
  async handleWriteAttribute(data) {
    const { nodeId, endpointId, clusterId, attributeId, value } = data;
    logger.debug(
      `write_attribute for test node ${nodeId} on ${endpointId}/${clusterId}/${attributeId} - value: ${JSON.stringify(value)}`
    );
    return {
      endpointId,
      clusterId,
      attributeId,
      status: 0
      // Success
    };
  }
  /**
   * Invoke a command on a test node.
   * Logs the command and returns null (no actual command execution).
   */
  async handleInvoke(data) {
    const { nodeId, endpointId, clusterId, commandName, data: payload } = data;
    logger.debug(
      `device_command for test node ${nodeId} on endpoint ${endpointId} - cluster ${clusterId} - command ${commandName} - payload: ${JSON.stringify(payload)}`
    );
    return null;
  }
  /**
   * Get IP addresses for a test node.
   * Returns mock IP addresses.
   */
  async getNodeIpAddresses(_nodeId, _preferCache) {
    return ["0.0.0.0", "0000:1111:2222:3333:4444"];
  }
  /**
   * Ping a test node.
   * Returns mock success results.
   */
  async pingNode(_nodeId, _attempts) {
    return { "0.0.0.0": true, "0000:1111:2222:3333:4444": true };
  }
  /**
   * Remove a test node.
   */
  async removeNode(nodeId) {
    const bigId = BigInt(nodeId);
    if (!this.#testNodes.has(bigId)) {
      throw ServerError.nodeNotExists(nodeId);
    }
    logger.info(`Removing test node ${nodeId}`);
    this.#testNodes.delete(bigId);
    this.nodeRemoved.emit(nodeId);
  }
}
export {
  TestNodeCommandHandler
};
//# sourceMappingURL=TestNodeCommandHandler.js.map
