var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
var __runInitializers = (array, flags, self, value) => {
  for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
  return value;
};
var __decorateElement = (array, flags, name, decorators, target, extra) => {
  var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
  var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
  var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
  var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
    return __privateGet(this, extra);
  }, set [name](x) {
    return __privateSet(this, extra, x);
  } }, name));
  k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
  for (var i = decorators.length - 1; i >= 0; i--) {
    ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
    if (k) {
      ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
      if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
      if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
    }
    it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
    if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
    else if (typeof it !== "object" || it === null) __typeError("Object expected");
    else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
  }
  return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Supervision } from "#behavior/supervision/Supervision.js";
import { BasicSet, deepCopy, InternalError, Logger, MaybePromise, ObserverGroup, serialize } from "@matter/general";
import {
  any,
  bool,
  fabricIdx,
  field,
  groupId,
  int16,
  int32,
  int40,
  int48,
  int56,
  int64,
  int8,
  listOf,
  mandatory,
  map16,
  map32,
  map64,
  map8,
  nonvolatile,
  string,
  uint16,
  uint24,
  uint32,
  uint40,
  uint48,
  uint56,
  uint64,
  uint8
} from "@matter/model";
import { assertRemoteActor, FabricManager, GroupSession } from "@matter/protocol";
import {
  AttributeId,
  ClusterId,
  GroupId,
  NullableSchema,
  Status,
  StatusResponse,
  TlvNumericSchema,
  TlvOfModel,
  ValidationOutOfBoundsError
} from "@matter/types";
import { ScenesManagement } from "@matter/types/clusters/scenes-management";
import { ScenesManagementBehavior } from "./ScenesManagementBehavior.js";
const logger = Logger.get("ScenesManagementServer");
const UNDEFINED_SCENE_ID = 255;
function constraintErrorWithSceneId(groupId2, sceneId) {
  const response = { status: Status.ConstraintError, groupId: groupId2, sceneId };
  Supervision(response, "sceneId").constraint = false;
  return response;
}
const GLOBAL_SCENE_ID = 0;
const UNDEFINED_GROUP = GroupId(0);
var AttributeValuePairDataFields = /* @__PURE__ */ ((AttributeValuePairDataFields2) => {
  AttributeValuePairDataFields2["ValueUnsigned8"] = "valueUnsigned8";
  AttributeValuePairDataFields2["ValueSigned8"] = "valueSigned8";
  AttributeValuePairDataFields2["ValueUnsigned16"] = "valueUnsigned16";
  AttributeValuePairDataFields2["ValueSigned16"] = "valueSigned16";
  AttributeValuePairDataFields2["ValueUnsigned32"] = "valueUnsigned32";
  AttributeValuePairDataFields2["ValueSigned32"] = "valueSigned32";
  AttributeValuePairDataFields2["ValueUnsigned64"] = "valueUnsigned64";
  AttributeValuePairDataFields2["ValueSigned64"] = "valueSigned64";
  return AttributeValuePairDataFields2;
})(AttributeValuePairDataFields || {});
const DataTypeToSceneAttributeDataMap = {
  [bool.name]: "valueUnsigned8" /* ValueUnsigned8 */,
  [map8.name]: "valueUnsigned8" /* ValueUnsigned8 */,
  [uint8.name]: "valueUnsigned8" /* ValueUnsigned8 */,
  [int8.name]: "valueSigned8" /* ValueSigned8 */,
  [uint16.name]: "valueUnsigned16" /* ValueUnsigned16 */,
  [map16.name]: "valueUnsigned16" /* ValueUnsigned16 */,
  [int16.name]: "valueSigned16" /* ValueSigned16 */,
  [uint24.name]: "valueUnsigned32" /* ValueUnsigned32 */,
  [uint32.name]: "valueUnsigned32" /* ValueUnsigned32 */,
  [map32.name]: "valueUnsigned32" /* ValueUnsigned32 */,
  [int32.name]: "valueSigned32" /* ValueSigned32 */,
  [uint40.name]: "valueUnsigned64" /* ValueUnsigned64 */,
  [uint48.name]: "valueUnsigned64" /* ValueUnsigned64 */,
  [uint56.name]: "valueUnsigned64" /* ValueUnsigned64 */,
  [uint64.name]: "valueUnsigned64" /* ValueUnsigned64 */,
  [map64.name]: "valueUnsigned64" /* ValueUnsigned64 */,
  [int40.name]: "valueSigned64" /* ValueSigned64 */,
  [int48.name]: "valueSigned64" /* ValueSigned64 */,
  [int56.name]: "valueSigned64" /* ValueSigned64 */,
  [int64.name]: "valueSigned64" /* ValueSigned64 */
};
const { commands } = ScenesManagement.schema;
const addScene = commands.require("AddScene");
const viewScene = commands.require("ViewScene");
const removeScene = commands.require("RemoveScene");
const storeScene = commands.require("StoreScene");
const copyScene = commands.require("CopyScene");
const ScenesManagementSchema = ScenesManagement.schema.extend(
  void 0,
  addScene.extend(
    void 0,
    addScene.fields.extend("SceneId", { constraint: "none" }),
    addScene.fields.extend("TransitionTime", { constraint: "none" }),
    addScene.fields.extend("SceneName", { constraint: "none" })
  ),
  viewScene.extend(void 0, viewScene.fields.extend("SceneId", { constraint: "none" })),
  removeScene.extend(void 0, removeScene.fields.extend("SceneId", { constraint: "none" })),
  storeScene.extend(void 0, storeScene.fields.extend("SceneId", { constraint: "none" })),
  copyScene.extend(
    void 0,
    copyScene.fields.extend("SceneIdentifierFrom", { constraint: "none" }),
    copyScene.fields.extend("SceneIdentifierTo", { constraint: "none" })
  )
);
const ScenesManagementBase = ScenesManagementBehavior.for(ScenesManagement, ScenesManagementSchema).with(
  ScenesManagement.Feature.SceneNames
);
class ScenesManagementServer extends ScenesManagementBase {
  initialize() {
    if (!this.state.sceneTableSize) {
      this.state.sceneTableSize = 128;
    }
    const fabricManager = this.endpoint.env.get(FabricManager);
    this.#initializeFabricSceneInfo(fabricManager);
    this.reactTo(fabricManager.events.deleting, this.#handleDeleteFabric);
  }
  /**
   * Handles removal of one group in a fabric.
   * This method is called by the GroupsServer implementation and also internally by this cluster.
   */
  removeScenesForGroupOnFabric(fabricIndex, groupId2) {
    this.state.sceneTable = this.state.sceneTable.filter(
      (s) => !(s.fabricIndex === fabricIndex && s.sceneGroupId === groupId2)
    );
    if (this.internal.monitorSceneAttributesForFabric === fabricIndex) {
      const fabricSceneInfo = this.#fabricSceneInfoForFabric(fabricIndex);
      if (fabricSceneInfo !== void 0) {
        if (fabricSceneInfo.currentGroup === groupId2 && fabricSceneInfo.sceneValid) {
          fabricSceneInfo.sceneValid = false;
          this.internal.monitorSceneAttributesForFabric = null;
        }
      }
    }
    this.#updateFabricSceneInfoCountsForFabric(fabricIndex);
  }
  /** Handles removal of all groups in a fabric. This method is called by the GroupsServer implementation. */
  removeScenesForAllGroupsForFabric(fabricIndex) {
    this.state.sceneTable = this.state.sceneTable.filter((s) => s.fabricIndex !== fabricIndex);
    this.#invalidateFabricSceneInfoForFabric(fabricIndex);
    this.#updateFabricSceneInfoCountsForFabric(fabricIndex);
  }
  /** Validates the groupId and sceneId parameters of scene commands and returns convenient data for further processing */
  #assertSceneCommandParameter(groupIdToValidate, sceneId) {
    assertRemoteActor(this.context);
    const fabric = this.context.session.associatedFabric;
    const fabricIndex = fabric.fabricIndex;
    const isGroupSession = GroupSession.is(this.context.session);
    let groupId2 = void 0;
    if (isGroupSession && groupIdToValidate === 0) {
      throw new StatusResponse.InvalidCommandError(`GroupId cannot be 0 in a Group Session`);
    }
    if (groupIdToValidate === 0 || this.#groupExistentInFabric(fabric, groupIdToValidate)) {
      groupId2 = groupIdToValidate;
    }
    const existingSceneIndex = groupId2 !== void 0 && sceneId !== void 0 ? this.#sceneIndexForId(fabricIndex, sceneId, groupId2) : -1;
    return {
      fabric,
      fabricIndex,
      groupId: groupId2,
      existingSceneIndex
    };
  }
  /**
   * Adds or replaces a scene entry in the scene table.
   * If existingSceneIndex is -1, a new entry is added, else replaces the existing entry at that index.
   * It also checks if the scene is allowed to be added depending on the fabric scene capacity.
   *
   * @returns The AddSceneResponse compatible response of the action
   */
  #addOrReplaceSceneEntry(sceneData, existingSceneIndex = -1) {
    const { fabricIndex, sceneGroupId: groupId2, sceneId } = sceneData;
    if (existingSceneIndex === -1) {
      if (this.#scenesForFabric(fabricIndex).length >= this.#fabricSceneCapacity) {
        return { status: Status.ResourceExhausted, groupId: groupId2, sceneId };
      }
      this.state.sceneTable.push(sceneData);
      logger.debug(`Added scene ${sceneId} in group ${groupId2} for fabric ${fabricIndex}`);
      this.#updateFabricSceneInfoCountsForFabric(fabricIndex);
    } else {
      this.state.sceneTable[existingSceneIndex] = sceneData;
      logger.debug(`Updated scene ${sceneId} in group ${groupId2} for fabric ${fabricIndex}`);
    }
    return { status: Status.Success, groupId: groupId2, sceneId };
  }
  /** Implements the AddScene command */
  addScene({
    groupId: reqGroupId,
    sceneId,
    sceneName,
    transitionTime,
    extensionFieldSetStructs
  }) {
    if (sceneId > 254 || transitionTime > 6e7) {
      return constraintErrorWithSceneId(reqGroupId, sceneId);
    }
    const { fabricIndex, groupId: groupId2, existingSceneIndex } = this.#assertSceneCommandParameter(reqGroupId, sceneId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId, sceneId };
    }
    const sceneValues = this.#decodeExtensionFieldSets(extensionFieldSetStructs);
    if (sceneValues === void 0) {
      return { status: Status.InvalidCommand, groupId: groupId2, sceneId };
    }
    return this.#addOrReplaceSceneEntry(
      {
        sceneGroupId: groupId2,
        sceneId,
        sceneName,
        sceneTransitionTime: transitionTime,
        sceneValues,
        fabricIndex
      },
      existingSceneIndex
    );
  }
  /** Implements the ViewScene command */
  viewScene({
    groupId: reqGroupId,
    sceneId
  }) {
    if (sceneId > 254) {
      return constraintErrorWithSceneId(reqGroupId, sceneId);
    }
    const { groupId: groupId2, existingSceneIndex } = this.#assertSceneCommandParameter(reqGroupId, sceneId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId, sceneId };
    }
    if (existingSceneIndex === -1) {
      return { status: Status.NotFound, groupId: groupId2, sceneId };
    }
    const scene = this.state.sceneTable[existingSceneIndex];
    return {
      status: Status.Success,
      groupId: scene.sceneGroupId,
      sceneId: scene.sceneId,
      sceneName: scene.sceneName,
      transitionTime: scene.sceneTransitionTime,
      extensionFieldSetStructs: this.#encodeExtensionFieldSets(scene.sceneValues)
    };
  }
  /** Implements the RemoveScene command */
  removeScene({
    groupId: reqGroupId,
    sceneId
  }) {
    if (sceneId > 254) {
      return constraintErrorWithSceneId(reqGroupId, sceneId);
    }
    const { groupId: groupId2, existingSceneIndex, fabricIndex } = this.#assertSceneCommandParameter(reqGroupId, sceneId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId, sceneId };
    }
    if (existingSceneIndex === -1) {
      return { status: Status.NotFound, groupId: groupId2, sceneId };
    }
    this.state.sceneTable.splice(existingSceneIndex, 1);
    if (this.internal.monitorSceneAttributesForFabric === fabricIndex) {
      const info = this.#fabricSceneInfoForFabric(fabricIndex);
      if (info) {
        if (info.currentGroup === groupId2 && info.currentScene === sceneId && info.sceneValid) {
          info.sceneValid = false;
          this.internal.monitorSceneAttributesForFabric = null;
        }
      }
    }
    this.#updateFabricSceneInfoCountsForFabric(fabricIndex);
    return { status: Status.Success, groupId: groupId2, sceneId };
  }
  /** Implements the RemoveAllScenes command */
  removeAllScenes({
    groupId: reqGroupId
  }) {
    const { groupId: groupId2, fabricIndex } = this.#assertSceneCommandParameter(reqGroupId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId };
    }
    this.removeScenesForGroupOnFabric(fabricIndex, groupId2);
    return { status: Status.Success, groupId: groupId2 };
  }
  /** Implements the StoreScene command */
  storeScene({
    groupId: reqGroupId,
    sceneId
  }) {
    if (sceneId > 254) {
      return constraintErrorWithSceneId(reqGroupId, sceneId);
    }
    const { groupId: groupId2, existingSceneIndex, fabricIndex } = this.#assertSceneCommandParameter(reqGroupId, sceneId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId, sceneId };
    }
    const sceneValues = this.#collectSceneAttributeValues();
    let result;
    if (existingSceneIndex !== -1) {
      const scene = this.state.sceneTable[existingSceneIndex];
      scene.sceneValues = sceneValues;
      result = this.#addOrReplaceSceneEntry(scene, existingSceneIndex);
    } else {
      result = this.#addOrReplaceSceneEntry({
        sceneGroupId: groupId2,
        sceneId,
        sceneName: "",
        sceneTransitionTime: 0,
        sceneValues,
        fabricIndex
      });
    }
    if (result.status === Status.Success) {
      this.#activateSceneInFabricSceneInfo(fabricIndex, groupId2, sceneId);
    }
    return result;
  }
  /** Implements the RecallScene command */
  async recallScene({ groupId: reqGroupId, sceneId, transitionTime }) {
    if (sceneId > 254) {
      throw new StatusResponse.ConstraintErrorError(`SceneId ${sceneId} exceeds maximum`);
    }
    if (transitionTime !== null && transitionTime !== void 0 && transitionTime > 6e7) {
      throw new StatusResponse.ConstraintErrorError(`TransitionTime ${transitionTime} exceeds maximum`);
    }
    const { groupId: groupId2, existingSceneIndex, fabricIndex } = this.#assertSceneCommandParameter(reqGroupId, sceneId);
    if (groupId2 === void 0) {
      throw new StatusResponse.InvalidCommandError(`Invalid groupId ${reqGroupId}`);
    }
    if (existingSceneIndex === -1) {
      throw new StatusResponse.NotFoundError(`SceneId ${sceneId} in groupId ${groupId2} not found`);
    }
    const scene = this.state.sceneTable[existingSceneIndex];
    await this.#applySceneAttributeValues(scene.sceneValues, transitionTime ?? scene.sceneTransitionTime);
    this.#activateSceneInFabricSceneInfo(fabricIndex, groupId2, sceneId);
  }
  /** Implements the GetSceneMembership command */
  getSceneMembership({
    groupId: reqGroupId
  }) {
    const { groupId: groupId2, fabricIndex } = this.#assertSceneCommandParameter(reqGroupId);
    if (groupId2 === void 0) {
      return { status: Status.InvalidCommand, groupId: reqGroupId, capacity: null };
    }
    const capacity = Math.max(
      Math.min(this.#fabricSceneCapacity - this.#scenesForFabric(fabricIndex).length, 254),
      0
    );
    return {
      status: Status.Success,
      groupId: groupId2,
      sceneList: this.#scenesForGroup(fabricIndex, groupId2).map(({ sceneId }) => sceneId),
      capacity
    };
  }
  /** Implements the CopyScene command */
  copyScene({
    mode,
    groupIdentifierFrom,
    sceneIdentifierFrom,
    groupIdentifierTo,
    sceneIdentifierTo
  }) {
    const {
      fabricIndex,
      groupId: fromGroupId,
      existingSceneIndex: fromSceneIndex
    } = this.#assertSceneCommandParameter(groupIdentifierFrom, sceneIdentifierFrom);
    const { groupId: toGroupId, existingSceneIndex: toSceneIndex } = this.#assertSceneCommandParameter(
      groupIdentifierTo,
      sceneIdentifierTo
    );
    if (fromGroupId === void 0 || toGroupId === void 0) {
      return { status: Status.InvalidCommand, groupIdentifierFrom, sceneIdentifierFrom };
    }
    if (mode.copyAllScenes) {
      const groupScenes = deepCopy(this.#scenesForGroup(fabricIndex, fromGroupId));
      for (const scene2 of groupScenes) {
        scene2.sceneGroupId = toGroupId;
        const { status } = this.#addOrReplaceSceneEntry(
          scene2,
          this.#sceneIndexForId(fabricIndex, scene2.sceneId, toGroupId)
        );
        if (status !== Status.Success) {
          return {
            status,
            groupIdentifierFrom,
            sceneIdentifierFrom
          };
        }
      }
      return {
        status: Status.Success,
        groupIdentifierFrom,
        sceneIdentifierFrom
      };
    }
    if (sceneIdentifierTo > 254 || sceneIdentifierFrom > 254) {
      return { status: Status.ConstraintError, groupIdentifierFrom, sceneIdentifierFrom };
    }
    if (fromSceneIndex === -1) {
      return { status: Status.NotFound, groupIdentifierFrom, sceneIdentifierFrom };
    }
    const scene = deepCopy(this.state.sceneTable[fromSceneIndex]);
    scene.sceneGroupId = groupIdentifierTo;
    scene.sceneId = sceneIdentifierTo;
    const result = this.#addOrReplaceSceneEntry(scene, toSceneIndex);
    return {
      status: result.status,
      groupIdentifierFrom,
      sceneIdentifierFrom
    };
  }
  /** Close the observers */
  async [Symbol.asyncDispose]() {
    this.internal.endpointSceneAttributeObservers.close();
  }
  /** Method used by the OnOff cluster to recall the global scene */
  async recallGlobalScene(fabricIndex) {
    const existingSceneIndex = this.#sceneIndexForId(fabricIndex, GLOBAL_SCENE_ID, UNDEFINED_GROUP);
    if (existingSceneIndex === -1) {
      return;
    }
    const scene = this.state.sceneTable[existingSceneIndex];
    await this.#applySceneAttributeValues(scene.sceneValues, scene.sceneTransitionTime);
    this.#activateSceneInFabricSceneInfo(fabricIndex, UNDEFINED_GROUP, GLOBAL_SCENE_ID);
  }
  /** Method used by the OnOff cluster to store the global scene */
  storeGlobalScene(fabricIndex) {
    const sceneValues = this.#collectSceneAttributeValues();
    const existingSceneIndex = this.#sceneIndexForId(fabricIndex, GLOBAL_SCENE_ID, UNDEFINED_GROUP);
    if (existingSceneIndex === -1) {
      this.#addOrReplaceSceneEntry({
        sceneGroupId: UNDEFINED_GROUP,
        sceneId: GLOBAL_SCENE_ID,
        sceneName: "Global Scene",
        sceneTransitionTime: 0,
        sceneValues,
        fabricIndex
      });
    } else {
      const scene = this.state.sceneTable[existingSceneIndex];
      scene.sceneValues = sceneValues;
      this.#addOrReplaceSceneEntry(scene, existingSceneIndex);
    }
  }
  /**
   * Decodes an ExtensionFieldSet struct into SceneAttributeData format including validation.
   * Returns undefined if the data are considered invalid according to the Spec/SDK.
   */
  #decodeExtensionFieldSets(fieldSet = []) {
    const result = {};
    for (const { clusterId, attributeValueList } of fieldSet) {
      const sceneClusterDetails = this.internal.endpointSceneableBehaviors.get("id", clusterId);
      if (sceneClusterDetails === void 0) {
        continue;
      }
      const clusterName = sceneClusterDetails.name;
      if (result[clusterName]) {
        delete result[clusterName];
      }
      for (const attributeValue of attributeValueList) {
        const { attributeId } = attributeValue;
        const attributeDetails = sceneClusterDetails.attributes.get("id", attributeId);
        if (attributeDetails === void 0) {
          return void 0;
        }
        const value = this.#decodeValueFromAttributeValuePair(attributeValue, attributeDetails);
        if (value == void 0) {
          return void 0;
        }
        result[clusterName] = result[clusterName] || {};
        result[clusterName][attributeDetails.name] = value;
      }
    }
    return result;
  }
  /**
   * Decodes and validates a single AttributeValuePair into the actual attribute value including validation.
   */
  #decodeValueFromAttributeValuePair(attributeValuePair, { schema, type, mappedType, nullable }) {
    let fieldCount = 0;
    for (const value2 of Object.values(attributeValuePair)) {
      if (value2 !== void 0) {
        fieldCount++;
      }
    }
    if (fieldCount !== 2) {
      logger.warn(
        `AttributeValuePair has invalid number (${fieldCount}) of fields (${serialize(attributeValuePair)})`
      );
      return void 0;
    }
    const value = attributeValuePair[mappedType];
    if (value === void 0) {
      logger.warn(
        `AttributeValuePair missing value for mappedType ${mappedType} (${serialize(attributeValuePair)})`
      );
      return void 0;
    }
    if (typeof value !== "number" && typeof value !== "bigint") {
      logger.warn(
        `AttributeValuePair has invalid non-numeric value for mappedType ${mappedType} (${serialize(attributeValuePair)})`
        // Should never happen
      );
      return void 0;
    }
    if (type === bool.name) {
      let boolValue;
      if (value === 0 || value === 1) {
        boolValue = !!value;
      } else if (nullable) {
        boolValue = null;
      } else {
        boolValue = false;
      }
      schema.validate(boolValue);
      return boolValue;
    }
    try {
      schema.validate(value);
      return value;
    } catch (error) {
      ValidationOutOfBoundsError.accept(error);
    }
    if (nullable) {
      return null;
    }
    if (!(schema instanceof TlvNumericSchema) || schema.min === void 0 || schema.max === void 0) {
      throw new InternalError(`Attribute schema for non-boolean non-nullable attribute is not TlvNumericSchema`);
    }
    const effectiveMin = schema.min;
    const effectiveMax = schema.max;
    let minDiff = BigInt(value) - BigInt(effectiveMin);
    if (minDiff < 0) {
      minDiff = -minDiff;
    }
    let maxDiff = BigInt(value) - BigInt(effectiveMax);
    if (maxDiff < 0) {
      maxDiff = -maxDiff;
    }
    let closestValue = effectiveMin;
    if (maxDiff < minDiff || maxDiff === minDiff && effectiveMax < closestValue) {
      closestValue = effectiveMax;
    }
    schema.validate(closestValue);
    return closestValue;
  }
  /** Encode the SceneAttributeData into ExtensionFieldSet structs for command responses */
  #encodeExtensionFieldSets(sceneValues) {
    const extensionFieldSetStructs = new Array();
    for (const [clusterName, clusterAttributes] of Object.entries(sceneValues)) {
      const clusterData = this.internal.endpointSceneableBehaviors.get("name", clusterName);
      if (clusterData === void 0) {
        throw new InternalError(
          `Scene Attribute cluster ${clusterName} not found on Endpoint ${this.endpoint.id} during encoding`
        );
      }
      const attributeValueList = new Array();
      for (const [attributeName, value] of Object.entries(clusterAttributes)) {
        const attributeDetails = clusterData.attributes.get("name", attributeName);
        if (attributeDetails !== void 0) {
          const encodedData = this.#encodeSceneAttributeValue(attributeDetails, value);
          if (encodedData !== void 0) {
            attributeValueList.push(encodedData);
          }
        }
      }
      if (attributeValueList.length) {
        extensionFieldSetStructs.push({
          clusterId: clusterData.id,
          attributeValueList
        });
      }
    }
    return extensionFieldSetStructs;
  }
  /** Encodes a single attribute value into an AttributeValuePair for command responses */
  #encodeSceneAttributeValue({ id: attributeId, schema, type, mappedType }, value) {
    if (type === bool.name) {
      if (value === null) {
        return { attributeId, [mappedType]: 255 };
      }
      return { attributeId, [mappedType]: value ? 1 : 0 };
    }
    if (value !== null) {
      return { attributeId, [mappedType]: value };
    }
    if (!(schema instanceof NullableSchema)) {
      throw new InternalError(`Attribute schema for non-nullable attribute is not NullableSchema`);
    }
    if (!(schema.schema instanceof TlvNumericSchema)) {
      throw new InternalError(`Underlying schema for non-nullable attribute is not TlvNumericSchema`);
    }
    if (schema.schema.baseTypeMin === 0 && schema.schema.max < schema.schema.baseTypeMax) {
      return { attributeId, [mappedType]: schema.schema.baseTypeMax };
    } else if (schema.schema.baseTypeMin < 0 && schema.schema.min > schema.schema.baseTypeMin) {
      return { attributeId, [mappedType]: schema.schema.baseTypeMin };
    } else {
      logger.warn(
        `Cannot determine out-of-bounds value for attribute schema, returning min value of datatype schema`
      );
    }
  }
  /** Collects the current values of all sceneable attributes on the endpoint */
  #collectSceneAttributeValues() {
    const sceneValues = {};
    this.endpoint.act((agent) => {
      for (const { name: clusterName, attributes } of this.internal.endpointSceneableBehaviors) {
        const clusterState = agent[clusterName].state;
        for (const attribute of attributes) {
          const attributeName = attribute.name;
          const currentValue = clusterState[attributeName];
          if (currentValue !== void 0) {
            sceneValues[clusterName] = sceneValues[clusterName] || {};
            sceneValues[clusterName][attributeName] = deepCopy(currentValue);
          }
        }
      }
    });
    logger.debug(`Collected scene attribute values on Endpoint ${this.endpoint.id}: ${serialize(sceneValues)}`);
    return sceneValues;
  }
  /**
   * Main method for Clusters to Register themselves with their "Apply Scenes Callback".
   *
   * @param behavior ClusterBehavior implementing a cluster with sceneable attributes
   * @param applyFunc Function that applies scene values for that cluster
   */
  implementScenes(behavior, applyFunc) {
    const { type } = behavior;
    if (!type.schema.id) {
      return;
    }
    const clusterName = type.schema.propertyName;
    const clusterId = ClusterId(type.schema.id);
    let sceneClusterDetails;
    for (const attribute of type.schema.conformant.attributes) {
      if (!attribute.effectiveQuality.scene) {
        continue;
      }
      const attributeId = AttributeId(attribute.id);
      const attributeName = attribute.propertyName;
      const event = this.endpoint.events[clusterName]?.[`${attributeName}$Changed`];
      const hasValue = this.endpoint.state[clusterName]?.[attributeName] !== void 0;
      if (!hasValue || !event) {
        continue;
      }
      if (!sceneClusterDetails) {
        sceneClusterDetails = this.internal.endpointSceneableBehaviors.get("id", clusterId) ?? {
          id: clusterId,
          name: clusterName,
          attributes: new BasicSet(),
          clusterBehaviorType: type,
          applyFunc
        };
      }
      const attrType = attribute.primitiveBase?.name;
      if (attrType === void 0 || DataTypeToSceneAttributeDataMap[attrType] === void 0) {
        logger.warn(
          `Scene Attribute ${attribute.name} on Cluster ${clusterName} has unsupported datatype ${attrType} for scene management on Endpoint ${this.endpoint.id}`
        );
        continue;
      }
      sceneClusterDetails.attributes.add({
        id: attributeId,
        name: attributeName,
        schema: TlvOfModel(attribute),
        type: attrType,
        mappedType: DataTypeToSceneAttributeDataMap[attrType],
        nullable: !!attribute.effectiveQuality.nullable
      });
    }
    if (sceneClusterDetails) {
      logger.info(
        `Registered ${sceneClusterDetails.attributes.size} scene attributes for Cluster ${clusterName} on Endpoint ${this.endpoint.id}`
      );
      this.internal.endpointSceneableBehaviors.add(sceneClusterDetails);
    }
  }
  /** Apply scene attribute values in the various clusters on the endpoint. */
  #applySceneAttributeValues(sceneValues, transitionTime = null) {
    logger.debug(`Recalling scene on Endpoint ${this.endpoint.id} with values: ${serialize(sceneValues)}`);
    const agent = this.endpoint.agentFor(this.context);
    const promises = [];
    for (const [clusterName, clusterAttributes] of Object.entries(sceneValues)) {
      const { applyFunc, clusterBehaviorType } = this.internal.endpointSceneableBehaviors.get("name", clusterName) ?? {};
      if (applyFunc && clusterBehaviorType) {
        const result = applyFunc.call(agent.get(clusterBehaviorType), clusterAttributes, transitionTime ?? 0);
        if (MaybePromise.is(result)) {
          promises.push(result);
        }
      } else {
        logger.warn(
          `No scenes implementation found for cluster ${clusterName} on Endpoint ${this.endpoint.id} during scene recall. Values are ignored`
        );
      }
    }
    if (promises.length) {
      return Promise.all(promises).then(
        () => void 0,
        (error) => logger.warn(`Error applying scene attribute values on Endpoint ${this.endpoint.id}:`, error)
      );
    }
  }
  #groupExistentInFabric(fabric, groupId2) {
    return fabric.groups.groupKeyIdMap.has(groupId2);
  }
  /**
   * The Scene Table capacity for a given fabric SHALL be less than half (rounded down towards 0) of the Scene Table
   * entries (as indicated in the SceneTableSize attribute), with a maximum of 253 entries
   */
  get #fabricSceneCapacity() {
    return Math.min(Math.floor((this.state.sceneTableSize - 1) / 2), 253);
  }
  #scenesForGroup(fabricIndex, groupId2) {
    return this.state.sceneTable.filter((s) => s.fabricIndex === fabricIndex && s.sceneGroupId === groupId2);
  }
  #scenesForFabric(fabricIndex) {
    return this.state.sceneTable.filter((s) => s.fabricIndex === fabricIndex);
  }
  #sceneIndexForId(fabricIndex, sceneId, groupId2) {
    return this.state.sceneTable.findIndex(
      (s) => s.fabricIndex === fabricIndex && s.sceneId === sceneId && s.sceneGroupId === groupId2
    );
  }
  #fabricSceneInfoForFabric(fabricIndex) {
    return this.state.fabricSceneInfo.find((f) => f.fabricIndex === fabricIndex);
  }
  /** If the fabric is the one that currently has a valid scene being monitored, invalidate it. */
  #invalidateFabricSceneInfoForFabric(fabricIndex) {
    if (this.internal.monitorSceneAttributesForFabric !== fabricIndex) {
      return;
    }
    const infoEntry = this.#fabricSceneInfoForFabric(fabricIndex);
    if (infoEntry && infoEntry.sceneValid) {
      infoEntry.sceneValid = false;
    }
    this.internal.monitorSceneAttributesForFabric = null;
  }
  /**
   * Invalidate all fabric scene info entries.
   * Method will be called by relevant clusters when commands change the state.
   */
  makeAllFabricSceneInfoEntriesInvalid() {
    if (this.internal.monitorSceneAttributesForFabric === null) {
      return;
    }
    const infoEntry = this.#fabricSceneInfoForFabric(this.internal.monitorSceneAttributesForFabric);
    if (infoEntry && infoEntry.sceneValid) {
      infoEntry.sceneValid = false;
    }
    this.internal.monitorSceneAttributesForFabric = null;
  }
  /** Initializes the fabric scene info entries based on existing fabrics and scene table. */
  #initializeFabricSceneInfo(fabric) {
    const existingEntries = /* @__PURE__ */ new Map();
    for (const entry of this.state.fabricSceneInfo) {
      existingEntries.set(entry.fabricIndex, entry);
    }
    const list = new Array();
    for (const { fabricIndex } of fabric.fabrics) {
      const entry = existingEntries.get(fabricIndex) ?? {
        sceneCount: 0,
        // Will be updated before it is set
        currentScene: UNDEFINED_SCENE_ID,
        currentGroup: UNDEFINED_GROUP,
        sceneValid: false,
        remainingCapacity: 0,
        // Will be updated before it is set
        fabricIndex
      };
      entry.sceneValid = false;
      const { sceneCount, remainingCapacity } = this.#countsForFabric(fabricIndex);
      entry.sceneCount = sceneCount;
      entry.remainingCapacity = remainingCapacity;
      list.push(entry);
    }
    this.state.fabricSceneInfo = list;
  }
  /** Updates the scene count and remaining capacity for a given fabric index */
  #updateFabricSceneInfoCountsForFabric(fabricIndex) {
    const infoEntryIndex = this.state.fabricSceneInfo.findIndex((f) => f.fabricIndex === fabricIndex);
    const entry = infoEntryIndex !== -1 ? this.state.fabricSceneInfo[infoEntryIndex] : {
      sceneCount: 0,
      // Will be updated before it is set
      currentScene: UNDEFINED_SCENE_ID,
      currentGroup: UNDEFINED_GROUP,
      sceneValid: false,
      remainingCapacity: 0,
      // Will be updated before it is set
      fabricIndex
    };
    const { sceneCount, remainingCapacity } = this.#countsForFabric(fabricIndex);
    entry.sceneCount = sceneCount;
    entry.remainingCapacity = remainingCapacity;
    if (infoEntryIndex === -1) {
      this.state.fabricSceneInfo.push(entry);
    } else {
      this.state.fabricSceneInfo[infoEntryIndex] = entry;
    }
  }
  #countsForFabric(fabricIndex) {
    const sceneCount = this.#scenesForFabric(fabricIndex).length;
    return {
      sceneCount,
      remainingCapacity: Math.max(this.#fabricSceneCapacity - sceneCount, 0)
    };
  }
  /** Activates the given scene in the fabric scene info, invalidating all others. */
  #activateSceneInFabricSceneInfo(fabricIndex, groupId2, sceneId) {
    for (const infoEntry of this.state.fabricSceneInfo) {
      if (infoEntry.fabricIndex === fabricIndex) {
        infoEntry.currentGroup = groupId2;
        infoEntry.currentScene = sceneId;
        infoEntry.sceneValid = true;
        this.internal.monitorSceneAttributesForFabric = fabricIndex;
      } else if (infoEntry.sceneValid) {
        infoEntry.sceneValid = false;
      }
    }
  }
  /** Removes all scenes for a given fabric when the fabric is deleted */
  #handleDeleteFabric({ fabricIndex }) {
    if (this.internal.monitorSceneAttributesForFabric === fabricIndex) {
      this.internal.monitorSceneAttributesForFabric = null;
    }
  }
}
((ScenesManagementServer2) => {
  var _fabricIndex_dec, _sceneValues_dec, _sceneTransitionTime_dec, _sceneName_dec, _sceneId_dec, _sceneGroupId_dec, _init, _sceneTable_dec, _a, _init2;
  _sceneGroupId_dec = [field(groupId, mandatory)], _sceneId_dec = [field(uint8.extend({ constraint: "max 254" }), mandatory)], _sceneName_dec = [field(string.extend({ constraint: "max 16" }))], _sceneTransitionTime_dec = [field(uint32.extend({ constraint: "max 60000000" }), mandatory)], _sceneValues_dec = [field(any, mandatory)], _fabricIndex_dec = [field(fabricIdx, mandatory)];
  class ScenesTableEntry {
    constructor() {
      __publicField(this, "sceneGroupId", __runInitializers(_init, 8, this)), __runInitializers(_init, 11, this);
      __publicField(this, "sceneId", __runInitializers(_init, 12, this)), __runInitializers(_init, 15, this);
      __publicField(this, "sceneName", __runInitializers(_init, 16, this)), __runInitializers(_init, 19, this);
      __publicField(this, "sceneTransitionTime", __runInitializers(_init, 20, this)), __runInitializers(_init, 23, this);
      __publicField(this, "sceneValues", __runInitializers(_init, 24, this)), __runInitializers(_init, 27, this);
      __publicField(this, "fabricIndex", __runInitializers(_init, 28, this)), __runInitializers(_init, 31, this);
    }
  }
  _init = __decoratorStart(null);
  __decorateElement(_init, 5, "sceneGroupId", _sceneGroupId_dec, ScenesTableEntry);
  __decorateElement(_init, 5, "sceneId", _sceneId_dec, ScenesTableEntry);
  __decorateElement(_init, 5, "sceneName", _sceneName_dec, ScenesTableEntry);
  __decorateElement(_init, 5, "sceneTransitionTime", _sceneTransitionTime_dec, ScenesTableEntry);
  __decorateElement(_init, 5, "sceneValues", _sceneValues_dec, ScenesTableEntry);
  __decorateElement(_init, 5, "fabricIndex", _fabricIndex_dec, ScenesTableEntry);
  __decoratorMetadata(_init, ScenesTableEntry);
  ScenesManagementServer2.ScenesTableEntry = ScenesTableEntry;
  class State extends (_a = ScenesManagementBase.State, _sceneTable_dec = [field(listOf(ScenesTableEntry), nonvolatile, mandatory)], _a) {
    constructor() {
      super(...arguments);
      __publicField(this, "sceneTable", __runInitializers(_init2, 8, this, new Array())), __runInitializers(_init2, 11, this);
    }
  }
  _init2 = __decoratorStart(_a);
  __decorateElement(_init2, 5, "sceneTable", _sceneTable_dec, State);
  __decoratorMetadata(_init2, State);
  ScenesManagementServer2.State = State;
  class Internal {
    /** ObserverGroup for all $Changed events of sceneable attributes */
    endpointSceneAttributeObservers = new ObserverGroup();
    /** Fabric index where a scene is currently valid, if any */
    monitorSceneAttributesForFabric = null;
    /** Map of sceneable behaviors/clusters and their sceneable attributes on the endpoint */
    endpointSceneableBehaviors = new BasicSet();
  }
  ScenesManagementServer2.Internal = Internal;
})(ScenesManagementServer || (ScenesManagementServer = {}));
export {
  DataTypeToSceneAttributeDataMap,
  ScenesManagementServer
};
//# sourceMappingURL=ScenesManagementServer.js.map
