/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { GroupKeyManagementServer } from "#behaviors/group-key-management";
import { IdentifyBehavior } from "#behaviors/identify";
import { ScenesManagementServer } from "#behaviors/scenes-management";
import { RootEndpoint } from "#endpoints/root";
import { InternalError, Logger } from "@matter/general";
import { AccessLevel } from "@matter/model";
import { assertRemoteActor } from "@matter/protocol";
import {
  Command,
  StatusCode,
  StatusResponseError,
  TlvField,
  TlvGroupId,
  TlvNoResponse,
  TlvObject,
  TlvString
} from "@matter/types";
import { Groups } from "@matter/types/clusters/groups";
import { GroupsBehavior } from "./GroupsBehavior.js";
const logger = Logger.get("GroupsServer");
Groups.Cluster.commands = {
  ...Groups.Cluster.commands,
  addGroup: Command(
    0,
    TlvObject({
      groupId: TlvField(0, TlvGroupId),
      groupName: TlvField(1, TlvString)
    }),
    0,
    Groups.TlvAddGroupResponse,
    { invokeAcl: AccessLevel.Manage }
  ),
  addGroupIfIdentifying: Command(
    5,
    TlvObject({
      groupId: TlvField(0, TlvGroupId),
      groupName: TlvField(1, TlvString)
    }),
    5,
    TlvNoResponse,
    { invokeAcl: AccessLevel.Manage }
  )
};
const GroupsBase = GroupsBehavior.with(Groups.Feature.GroupNames);
class GroupsServer extends GroupsBase {
  initialize() {
    this.state.nameSupport.groupNames = this.features.groupNames;
  }
  // We need to search the root here ourselves because we cannot include ServerNode because else we generate a
  // circular dependency
  get #rootEndpoint() {
    const rootEndpoint = this.endpoint.ownerOfType(RootEndpoint);
    if (rootEndpoint === void 0) {
      throw new InternalError("RootEndpoint not found");
    }
    return rootEndpoint;
  }
  async #actOnGroupKeyManagement(act) {
    assertRemoteActor(this.context);
    const agent = this.#rootEndpoint.agentFor(this.context);
    const gkm = agent.get(GroupKeyManagementServer);
    await agent.context.transaction.addResources(gkm);
    await agent.context.transaction.begin();
    return act(this.context.session.associatedFabric, gkm);
  }
  async addGroup({ groupId, groupName }) {
    assertRemoteActor(this.context);
    const fabric = this.context.session.associatedFabric;
    if (groupId < 1) {
      return { status: StatusCode.ConstraintError, groupId };
    }
    if (groupName.length > 16) {
      return { status: StatusCode.ConstraintError, groupId };
    }
    if (!fabric.groups.groupKeyIdMap.has(groupId)) {
      return { status: StatusCode.UnsupportedAccess, groupId };
    }
    const endpointNumber = this.endpoint.number;
    try {
      await this.#actOnGroupKeyManagement(
        (fabric2, gkm) => gkm.addEndpointForGroup(fabric2, groupId, endpointNumber, groupName)
      );
    } catch (error) {
      logger.error(error);
      StatusResponseError.accept(error);
      return { status: error.code, groupId };
    }
    return { status: StatusCode.Success, groupId };
  }
  viewGroup({ groupId }) {
    assertRemoteActor(this.context);
    const fabric = this.context.session.associatedFabric;
    if (groupId < 1) {
      return { status: StatusCode.ConstraintError, groupId, groupName: "" };
    }
    const fabricIndex = fabric.fabricIndex;
    const endpointNumber = this.endpoint.number;
    const { groupTable } = this.#rootEndpoint.stateOf(GroupKeyManagementServer);
    const groupEntry = groupTable.find((entry) => entry.groupId === groupId && entry.fabricIndex === fabricIndex);
    if (groupEntry === void 0 || !groupEntry.endpoints.includes(endpointNumber)) {
      return { status: StatusCode.NotFound, groupId, groupName: "" };
    }
    return { status: StatusCode.Success, groupId, groupName: groupEntry.groupName ?? "" };
  }
  async getGroupMembership({
    groupList
  }) {
    assertRemoteActor(this.context);
    const fabric = this.context.session.associatedFabric;
    const fabricIndex = fabric.fabricIndex;
    const endpointNumber = this.endpoint.number;
    const { groupTable } = this.#rootEndpoint.stateOf(GroupKeyManagementServer);
    const endpointGroups = groupTable.filter(
      (entry) => entry.endpoints.includes(endpointNumber) && entry.fabricIndex === fabricIndex
    );
    const fabricGroupsList = endpointGroups.map((entry) => entry.groupId);
    if (groupList.length === 0) {
      return { capacity: 254 - fabricGroupsList.length, groupList: fabricGroupsList };
    }
    const filteredGroupsList = groupList.filter((groupId) => fabricGroupsList.includes(groupId));
    return { capacity: 254 - fabricGroupsList.length, groupList: filteredGroupsList };
  }
  async removeGroup({ groupId }) {
    if (groupId < 1) {
      return { status: StatusCode.ConstraintError, groupId };
    }
    try {
      assertRemoteActor(this.context);
      if (await this.#actOnGroupKeyManagement(
        (fabric, gkm) => gkm.removeEndpoint(fabric, this.endpoint.number, groupId)
      )) {
        if (this.agent.has(ScenesManagementServer)) {
          this.agent.get(ScenesManagementServer).removeScenesForGroupOnFabric(this.context.session.associatedFabric.fabricIndex, groupId);
        }
        return { status: StatusCode.Success, groupId };
      }
      return { status: StatusCode.NotFound, groupId };
    } catch (error) {
      StatusResponseError.accept(error);
      return { status: error.code, groupId };
    }
  }
  async removeAllGroups() {
    try {
      assertRemoteActor(this.context);
      await this.#actOnGroupKeyManagement((fabric, gkm) => gkm.removeEndpoint(fabric, this.endpoint.number));
      if (this.agent.has(ScenesManagementServer)) {
        this.agent.get(ScenesManagementServer).removeScenesForAllGroupsForFabric(this.context.session.associatedFabric.fabricIndex);
      }
    } catch (error) {
      StatusResponseError.accept(error);
      throw error;
    }
  }
  async addGroupIfIdentifying({ groupId, groupName }) {
    if (this.endpoint.stateOf(IdentifyBehavior).identifyTime > 0) {
      const { status } = await this.addGroup({ groupId, groupName });
      if (status !== StatusCode.Success) {
        throw new StatusResponseError(`Failed to add group ${groupId}`, status);
      }
    }
  }
}
export {
  GroupsServer
};
//# sourceMappingURL=GroupsServer.js.map
