"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 ClusterServerBacking_exports = {};
__export(ClusterServerBacking_exports, {
  ClusterServerBacking: () => ClusterServerBacking
});
module.exports = __toCommonJS(ClusterServerBacking_exports);
var import_general = require("#general");
var import_model = require("#model");
var import_protocol = require("#protocol");
var import_types = require("#types");
var import_AccessControl = require("../AccessControl.js");
var import_ValidatedElements = require("../cluster/ValidatedElements.js");
var import_Contextual = require("../context/Contextual.js");
var import_StructManager = require("../state/managed/values/StructManager.js");
var import_Status = require("../state/transaction/Status.js");
var import_ServerBehaviorBacking = require("./ServerBehaviorBacking.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("Behavior");
class ClusterServerBacking extends import_ServerBehaviorBacking.ServerBehaviorBacking {
  #server;
  #clusterServer;
  get clusterServer() {
    return this.#clusterServer;
  }
  get runtime() {
    return this.#server.endpoint.env.runtime;
  }
  get server() {
    return this.#server;
  }
  get type() {
    return super.type;
  }
  constructor(server, type) {
    super(server.endpoint, type);
    this.#server = server;
  }
  invokeInitializer(behavior, options) {
    const { id, name, attributes, commands, events } = this.type.cluster;
    if (!options) {
      options = {};
    }
    const clusterServer = this.#clusterServer = {
      id,
      name,
      datasource: this.#createClusterDatasource(),
      attributes: {},
      commands: {},
      events: {}
    };
    const result = super.invokeInitializer(behavior, options);
    const createClusterServer = () => {
      behavior.state.eventList = [];
      const elements = new import_ValidatedElements.ValidatedElements(this.type, behavior);
      elements.report();
      this.#configureServers(
        elements.attributes,
        attributes,
        clusterServer.attributes,
        behavior,
        ["attributeList"],
        createAttributeServer
      );
      this.#configureServers(
        elements.commands,
        commands,
        clusterServer.commands,
        behavior,
        ["acceptedCommandList", "generatedCommandList"],
        createCommandServer
      );
      this.#configureServers(
        elements.events,
        events,
        clusterServer.events,
        behavior,
        ["eventList"],
        createEventServer
      );
      this.#server.addClusterServer(clusterServer);
    };
    if (import_general.MaybePromise.is(result)) {
      return result.then(createClusterServer);
    }
    createClusterServer();
  }
  get datasourceOptions() {
    return {
      ...super.datasourceOptions,
      cluster: this.type.cluster.id
    };
  }
  /**
   * Create the {@link ClusterDatasource} that adapts the Behavior API to the AttributeServer API.
   */
  #createClusterDatasource() {
    const eventHandler = this.eventHandler;
    const datasource = this.datasource;
    const env = this.endpoint.env;
    return {
      get version() {
        return datasource.version;
      },
      get eventHandler() {
        return eventHandler;
      },
      get fabrics() {
        return env.get(import_protocol.FabricManager).getFabrics();
      },
      // We handle change management ourselves
      changed() {
      },
      // We handle version management ourselves
      increaseVersion() {
        return datasource.version;
      }
    };
  }
  /**
   * Configure the servers for a set of elements (attributes, commands or events).
   */
  #configureServers(names, definitions, servers, behavior, attributeNames, addServer) {
    const collectedIds = Array();
    attributeNames.forEach(() => collectedIds.push(/* @__PURE__ */ new Set()));
    for (const name of names) {
      const definition = definitions[name];
      const { ids, server } = addServer(name, definition, this, behavior);
      ids.forEach((id, index) => collectedIds[index].add(id));
      servers[name] = server;
    }
    attributeNames.forEach((attributeName, index) => {
      behavior.state[attributeName] = [...collectedIds[index].values()];
    });
  }
}
function createAttributeServer(name, definition, backing, behavior) {
  function getter(_session, _endpoint, _isFabricFiltered, message) {
    if (!message) {
      return backing.datasource.view[name];
    }
    const behavior2 = behaviorFor(backing, message);
    behavior2.context.activity?.frame(`read ${name}`);
    const trace = behavior2.context.trace;
    if (trace) {
      trace.path = backing.path.at(name);
    }
    logger.debug("Read", import_general.Diagnostic.strong(`${backing}.state.${name}`), "via", behavior2.context.transaction.via);
    const state = behavior2.state;
    import_StructManager.StructManager.assertDirectReadAuthorized(state, name);
    if (trace) {
      trace.output = state[name];
    }
    return state[name];
  }
  function setter(value, _session, _endpoint, message) {
    const behavior2 = behaviorFor(backing, message);
    behavior2.context.activity?.frame(`write ${name}`);
    logger.info("Write", import_general.Diagnostic.strong(`${backing}.state.${name}`), "via", behavior2.context.transaction.via);
    const trace = behavior2.context.trace;
    if (trace) {
      trace.path = backing.path.at(name);
      trace.input = value;
    }
    const state = behavior2.state;
    state[name] = value;
    return behavior2.context.transaction?.status === import_Status.Status.Exclusive;
  }
  const server = (0, import_protocol.createAttributeServer)(
    backing.type.cluster,
    definition,
    name,
    behavior.state[name],
    backing.clusterServer.datasource,
    getter,
    setter
  );
  if (server instanceof import_protocol.AttributeServer) {
    const observable = backing.events[`${name}$Changed`];
    observable?.on((_value, _oldValue, context) => {
      const session = context.session;
      if (session instanceof import_protocol.SecureSession) {
        server.updated(session);
      } else {
        server.updatedLocal();
      }
    });
  }
  server.assignToEndpoint(backing.server);
  return {
    ids: [definition.id],
    server
  };
}
function createCommandServer(name, definition, backing) {
  const schema = backing.type.schema?.member((0, import_general.camelize)(name, true), [import_model.ElementTag.Command]);
  if (schema === void 0) {
    throw new import_general.ImplementationError(`There is no metadata for command ${name}`);
  }
  const access = (0, import_AccessControl.AccessControl)(schema);
  const handler = (request, _session, message) => {
    let requestDiagnostic;
    if ((0, import_general.isObject)(request)) {
      requestDiagnostic = import_general.Diagnostic.dict(request);
    } else if (request !== void 0) {
      requestDiagnostic = request;
    } else {
      requestDiagnostic = import_general.Diagnostic.weak("(no payload)");
    }
    const behavior = behaviorFor(backing, message);
    const path = backing.path.at(name);
    const trace = behavior.context.trace;
    if (trace) {
      trace.path = backing.path.at(name);
      trace.input = request;
    }
    logger.info("Invoke", import_general.Diagnostic.strong(path.toString()), behavior.context.transaction.via, requestDiagnostic);
    access.authorizeInvoke(behavior.context, {
      path,
      cluster: behavior.cluster.id
    });
    let isAsync = false;
    let activity;
    let result;
    try {
      activity = behavior.context?.activity?.frame(`invoke ${name}`);
      result = behavior[name](request);
      if (import_general.MaybePromise.is(result)) {
        isAsync = true;
        result = Promise.resolve(result).then((result2) => {
          if (trace) {
            trace.output = result2;
          }
          return result2;
        }).finally(() => activity?.[Symbol.dispose]());
      } else if (trace) {
        trace.output = result;
      }
    } finally {
      if (!isAsync) {
        activity?.[Symbol.dispose]();
      }
    }
    return result;
  };
  const server = new import_protocol.CommandServer(
    definition.requestId,
    definition.responseId,
    name,
    definition.requestSchema,
    definition.responseSchema,
    definition.timed,
    definition.invokeAcl,
    handler
  );
  server.debug = () => {
  };
  const ids = [definition.requestId];
  if (definition.responseSchema !== import_types.TlvNoResponse) {
    ids.push(definition.responseId);
  }
  return {
    ids,
    server
  };
}
function createEventServer(name, definition, backing) {
  const observable = backing.events[name];
  const server = new import_protocol.EventServer(
    definition.id,
    backing.type.cluster.id,
    name,
    definition.schema,
    definition.priority,
    definition.readAcl
  );
  observable?.on((payload, _context) => {
    const maybePromise = server.triggerEvent(payload);
    if (import_general.MaybePromise.is(maybePromise)) {
      backing.runtime.add(maybePromise);
    }
  });
  server.assignToEndpoint(backing.server);
  const promise = server.bindToEventHandler(backing.eventHandler);
  if (import_general.MaybePromise.is(promise)) {
    throw new import_general.InternalError("Event handler binding returned a promise");
  }
  return {
    ids: [definition.id],
    server
  };
}
function behaviorFor(backing, message) {
  const context = import_Contextual.Contextual.contextOf(message);
  if (!context) {
    throw new import_general.InternalError("Message context not installed");
  }
  const agent = context.agentFor(backing.endpoint);
  return agent.get(backing.type);
}
//# sourceMappingURL=ClusterServerBacking.js.map
