"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
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 UserCredential_exports = {};
__export(UserCredential_exports, {
  UserCredentialCCBehaviors: () => UserCredentialCCBehaviors,
  defaultCapabilities: () => defaultCapabilities
});
module.exports = __toCommonJS(UserCredential_exports);
var import_cc = require("@zwave-js/cc");
var import_UserCredentialCC = require("@zwave-js/cc/UserCredentialCC");
var import_core = require("@zwave-js/core");
var import_shared = require("@zwave-js/shared");
var import_testing = require("@zwave-js/testing");
const defaultCapabilities = {
  numberOfSupportedUsers: 10,
  supportedCredentialRules: [import_cc.UserCredentialRule.Single],
  maxUserNameLength: 32,
  supportsUserSchedule: false,
  supportsAllUsersChecksum: true,
  supportsUserChecksum: true,
  supportedUserNameEncodings: [import_cc.UserCredentialNameEncoding.ASCII],
  supportedUserTypes: [
    import_cc.UserCredentialUserType.General,
    import_cc.UserCredentialUserType.Programming,
    import_cc.UserCredentialUserType.NonAccess,
    import_cc.UserCredentialUserType.Duress,
    import_cc.UserCredentialUserType.Disposable,
    import_cc.UserCredentialUserType.Expiring,
    import_cc.UserCredentialUserType.RemoteOnly
  ],
  supportsCredentialChecksum: true,
  supportsAdminCode: true,
  supportsAdminCodeDeactivation: true,
  supportedCredentialTypes: /* @__PURE__ */ new Map([
    [import_cc.UserCredentialType.PINCode, {
      numberOfCredentialSlots: 10,
      minCredentialLength: 4,
      maxCredentialLength: 10,
      // CC:0083.01.04.11.019: CRB MUST be 1 when max hash length is 0
      maxCredentialHashLength: 0,
      supportsCredentialLearn: false
    }]
  ])
};
const STATE_KEY_PREFIX = "UserCredential_";
const StateKeys = {
  user: /* @__PURE__ */ __name((userId) => `${STATE_KEY_PREFIX}user_${userId}`, "user"),
  credential: /* @__PURE__ */ __name((userId, credentialType, credentialSlot) => `${STATE_KEY_PREFIX}cred_${userId}_${credentialType}_${credentialSlot}`, "credential"),
  adminPinCode: `${STATE_KEY_PREFIX}adminPinCode`,
  keyLockerEntry: /* @__PURE__ */ __name((entryType, entrySlot) => `${STATE_KEY_PREFIX}keyLocker_${entryType}_${entrySlot}`, "keyLockerEntry")
};
function getCapabilities(self, endpointIndex) {
  const caps = self.getCCCapabilities(import_core.CommandClasses["User Credential"], endpointIndex);
  return {
    ...defaultCapabilities,
    ...caps
  };
}
__name(getCapabilities, "getCapabilities");
function getUser(self, userId) {
  return self.state.get(StateKeys.user(userId));
}
__name(getUser, "getUser");
function setUser(self, userId, data) {
  self.state.set(StateKeys.user(userId), data);
}
__name(setUser, "setUser");
function deleteUser(self, userId) {
  self.state.delete(StateKeys.user(userId));
}
__name(deleteUser, "deleteUser");
function getCredential(self, userId, credentialType, credentialSlot) {
  return self.state.get(StateKeys.credential(userId, credentialType, credentialSlot));
}
__name(getCredential, "getCredential");
function setCredential(self, userId, credentialType, credentialSlot, data) {
  self.state.set(StateKeys.credential(userId, credentialType, credentialSlot), data);
}
__name(setCredential, "setCredential");
function deleteCredential(self, userId, credentialType, credentialSlot) {
  self.state.delete(StateKeys.credential(userId, credentialType, credentialSlot));
}
__name(deleteCredential, "deleteCredential");
function getAllUserIds(self, capabilities) {
  const userIds = [];
  for (let id = 1; id <= capabilities.numberOfSupportedUsers; id++) {
    if (getUser(self, id) !== void 0) {
      userIds.push(id);
    }
  }
  return userIds;
}
__name(getAllUserIds, "getAllUserIds");
function findNextUserId(self, afterId, capabilities) {
  for (let id = afterId + 1; id <= capabilities.numberOfSupportedUsers; id++) {
    if (getUser(self, id) !== void 0) {
      return id;
    }
  }
  return 0;
}
__name(findNextUserId, "findNextUserId");
function getAllCredentials(self, capabilities) {
  const result = [];
  const allUserIds = getAllUserIds(self, capabilities);
  for (const userId of allUserIds) {
    for (const [credentialType, typeCapabilities] of capabilities.supportedCredentialTypes) {
      for (let slot = 1; slot <= typeCapabilities.numberOfCredentialSlots; slot++) {
        if (getCredential(self, userId, credentialType, slot) !== void 0) {
          result.push({
            userId,
            credentialType,
            credentialSlot: slot
          });
        }
      }
    }
  }
  return result;
}
__name(getAllCredentials, "getAllCredentials");
function getCredentialsForUser(self, userId, capabilities) {
  return getAllCredentials(self, capabilities).filter((c) => c.userId === userId);
}
__name(getCredentialsForUser, "getCredentialsForUser");
function deleteCredentialsForUser(self, userId, capabilities) {
  for (const ref of getCredentialsForUser(self, userId, capabilities)) {
    deleteCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
  }
}
__name(deleteCredentialsForUser, "deleteCredentialsForUser");
function deleteCredentialsForType(self, credentialType, capabilities) {
  for (const ref of getAllCredentials(self, capabilities)) {
    if (ref.credentialType === credentialType) {
      deleteCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
    }
  }
}
__name(deleteCredentialsForType, "deleteCredentialsForType");
function deleteCredentialsForUserAndType(self, userId, credentialType, capabilities) {
  for (const ref of getCredentialsForUser(self, userId, capabilities)) {
    if (ref.credentialType === credentialType) {
      deleteCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
    }
  }
}
__name(deleteCredentialsForUserAndType, "deleteCredentialsForUserAndType");
function deleteAllCredentials(self, capabilities) {
  for (const ref of getAllCredentials(self, capabilities)) {
    deleteCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
  }
}
__name(deleteAllCredentials, "deleteAllCredentials");
function findNextCredential(self, afterType, afterSlot, capabilities, forUserId) {
  const creds = forUserId !== void 0 ? getCredentialsForUser(self, forUserId, capabilities) : getAllCredentials(self, capabilities);
  for (const ref of creds) {
    if (ref.credentialType > afterType || ref.credentialType === afterType && ref.credentialSlot > afterSlot) {
      return {
        nextCredentialType: ref.credentialType,
        nextCredentialSlot: ref.credentialSlot
      };
    }
  }
  return {
    nextCredentialType: import_cc.UserCredentialType.None,
    nextCredentialSlot: 0
  };
}
__name(findNextCredential, "findNextCredential");
function buildUserChecksumData(self, userId, capabilities) {
  const user = getUser(self, userId);
  if (!user)
    return new import_shared.Bytes();
  const nameBytes = import_shared.Bytes.from(user.userName, "utf-8");
  const headerLen = 5 + nameBytes.length;
  const header = new import_shared.Bytes(headerLen);
  header[0] = user.userType;
  header[1] = user.active ? 1 : 0;
  header[2] = user.credentialRule;
  header[3] = user.nameEncoding;
  header[4] = nameBytes.length;
  header.set(nameBytes, 5);
  const parts = [header];
  const creds = getCredentialsForUser(self, userId, capabilities);
  for (const ref of creds) {
    const credState = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
    if (!credState)
      continue;
    const credData = credState.credentialData;
    const credBuf = new import_shared.Bytes(4 + credData.length);
    credBuf[0] = ref.credentialType;
    credBuf.writeUInt16BE(ref.credentialSlot, 1);
    credBuf[3] = credData.length;
    credBuf.set(credData, 4);
    parts.push(credBuf);
  }
  return import_shared.Bytes.concat(parts);
}
__name(buildUserChecksumData, "buildUserChecksumData");
function buildCredentialChecksumData(self, credentialType, capabilities) {
  const allCreds = getAllCredentials(self, capabilities).filter((c) => c.credentialType === credentialType).toSorted((a, b) => a.credentialSlot - b.credentialSlot);
  const parts = [];
  for (const ref of allCreds) {
    const credState = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
    if (!credState)
      continue;
    const credData = credState.credentialData;
    const buf = new import_shared.Bytes(3 + credData.length);
    buf.writeUInt16BE(ref.credentialSlot, 0);
    buf[2] = credData.length;
    buf.set(credData, 3);
    parts.push(buf);
  }
  return import_shared.Bytes.concat(parts);
}
__name(buildCredentialChecksumData, "buildCredentialChecksumData");
function isDuplicateCredential(self, credentialType, credentialData, capabilities, excludeUserId, excludeSlot) {
  const allCreds = getAllCredentials(self, capabilities).filter((c) => c.credentialType === credentialType);
  for (const ref of allCreds) {
    if (ref.userId === excludeUserId && ref.credentialSlot === excludeSlot) {
      continue;
    }
    const existing = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
    if (existing && existing.credentialData.length === credentialData.length && existing.credentialData.every((b, i) => b === credentialData[i])) {
      return true;
    }
  }
  return false;
}
__name(isDuplicateCredential, "isDuplicateCredential");
function isDuplicateOfAdminCode(self, credentialData) {
  const adminCode = self.state.get(StateKeys.adminPinCode);
  if (!adminCode || adminCode.length === 0)
    return false;
  const adminBytes = import_shared.Bytes.from(adminCode, "ascii");
  return adminBytes.length === credentialData.length && adminBytes.every((b, i) => b === credentialData[i]);
}
__name(isDuplicateOfAdminCode, "isDuplicateOfAdminCode");
const respondToUserCapabilitiesGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCUserCapabilitiesGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      const cc = new import_UserCredentialCC.UserCredentialCCUserCapabilitiesReport({
        nodeId: controller.ownNodeId,
        // CC:0083.01.02.11.000: Number of supported UUIDs MUST NOT be zero
        numberOfSupportedUsers: capabilities.numberOfSupportedUsers,
        // CC:0083.01.02.11.001: MUST support at least one Credential Rule
        supportedCredentialRules: capabilities.supportedCredentialRules,
        // CC:0083.01.02.11.019: Max User Name length MUST be even (UTF-16)
        maxUserNameLength: capabilities.maxUserNameLength,
        supportsUserSchedule: capabilities.supportsUserSchedule ?? false,
        supportsAllUsersChecksum: capabilities.supportsAllUsersChecksum ?? false,
        supportsUserChecksum: capabilities.supportsUserChecksum ?? false,
        // CC:0083.01.02.11.026: At least one encoding MUST be supported
        supportedUserNameEncodings: capabilities.supportedUserNameEncodings ?? [import_cc.UserCredentialNameEncoding.ASCII],
        // CC:0083.01.02.11.017: MUST support at least one User Type
        supportedUserTypes: capabilities.supportedUserTypes ?? [import_cc.UserCredentialUserType.General]
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToCredentialCapabilitiesGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialCapabilitiesGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      const cc = new import_UserCredentialCC.UserCredentialCCCredentialCapabilitiesReport({
        nodeId: controller.ownNodeId,
        supportsCredentialChecksum: capabilities.supportsCredentialChecksum ?? false,
        supportsAdminCode: capabilities.supportsAdminCode ?? false,
        supportsAdminCodeDeactivation: capabilities.supportsAdminCodeDeactivation ?? false,
        credentialTypes: capabilities.supportedCredentialTypes
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToKeyLockerCapabilitiesGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCKeyLockerCapabilitiesGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      const cc = new import_UserCredentialCC.UserCredentialCCKeyLockerCapabilitiesReport({
        nodeId: controller.ownNodeId,
        keyLockerCapabilities: capabilities.supportedKeyLockerEntryTypes ?? /* @__PURE__ */ new Map()
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToUserGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCUserGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      const requestedUserId = receivedCC.userId;
      let targetUserId;
      if (requestedUserId === 0) {
        const allIds = getAllUserIds(self, capabilities);
        if (allIds.length === 0) {
          const cc2 = new import_UserCredentialCC.UserCredentialCCUserReport({
            nodeId: controller.ownNodeId,
            reportType: import_cc.UserCredentialUserReportType.ResponseToGet,
            nextUserId: 0,
            userId: 0,
            modifierType: import_cc.UserCredentialModifierType.DoesNotExist,
            modifierNodeId: 0,
            userType: import_cc.UserCredentialUserType.General,
            active: false,
            credentialRule: import_cc.UserCredentialRule.Single,
            expiringTimeoutMinutes: 0,
            nameEncoding: import_cc.UserCredentialNameEncoding.ASCII,
            userName: ""
          });
          return { action: "sendCC", cc: cc2 };
        }
        targetUserId = allIds[0];
      } else {
        targetUserId = requestedUserId;
      }
      const user = getUser(self, targetUserId);
      if (!user) {
        const cc2 = new import_UserCredentialCC.UserCredentialCCUserReport({
          nodeId: controller.ownNodeId,
          reportType: import_cc.UserCredentialUserReportType.ResponseToGet,
          nextUserId: findNextUserId(self, targetUserId, capabilities),
          userId: targetUserId,
          modifierType: import_cc.UserCredentialModifierType.DoesNotExist,
          modifierNodeId: 0,
          userType: import_cc.UserCredentialUserType.General,
          active: false,
          credentialRule: import_cc.UserCredentialRule.Single,
          expiringTimeoutMinutes: 0,
          nameEncoding: import_cc.UserCredentialNameEncoding.ASCII,
          userName: ""
        });
        return { action: "sendCC", cc: cc2 };
      }
      const cc = new import_UserCredentialCC.UserCredentialCCUserReport({
        nodeId: controller.ownNodeId,
        reportType: import_cc.UserCredentialUserReportType.ResponseToGet,
        nextUserId: findNextUserId(self, targetUserId, capabilities),
        userId: targetUserId,
        modifierType: user.modifierType,
        modifierNodeId: user.modifierNodeId,
        userType: user.userType,
        active: user.active,
        credentialRule: user.credentialRule,
        expiringTimeoutMinutes: user.expiringTimeoutMinutes,
        nameEncoding: user.nameEncoding,
        userName: user.userName
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToUserSet = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCUserSet))
      return;
    const setCC = receivedCC;
    const capabilities = getCapabilities(self, setCC.endpointIndex);
    const userId = setCC.userId;
    const operationType = setCC.operationType;
    if (operationType === import_cc.UserCredentialOperationType.Delete) {
      if (userId === 0) {
        const allIds = getAllUserIds(self, capabilities);
        for (const id of allIds) {
          deleteCredentialsForUser(self, id, capabilities);
          deleteUser(self, id);
        }
        const report3 = new import_UserCredentialCC.UserCredentialCCUserReport({
          nodeId: controller.ownNodeId,
          reportType: import_cc.UserCredentialUserReportType.UserDeleted,
          nextUserId: 0,
          userId: 0,
          modifierType: import_cc.UserCredentialModifierType.ZWave,
          modifierNodeId: controller.ownNodeId,
          userType: import_cc.UserCredentialUserType.General,
          active: false,
          credentialRule: import_cc.UserCredentialRule.Single,
          expiringTimeoutMinutes: 0,
          nameEncoding: import_cc.UserCredentialNameEncoding.ASCII,
          userName: ""
        });
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report3, {
          ackRequested: false
        }));
        return { action: "ok" };
      }
      if (userId > capabilities.numberOfSupportedUsers) {
        return { action: "fail" };
      }
      deleteCredentialsForUser(self, userId, capabilities);
      deleteUser(self, userId);
      const report2 = new import_UserCredentialCC.UserCredentialCCUserReport({
        nodeId: controller.ownNodeId,
        reportType: import_cc.UserCredentialUserReportType.UserDeleted,
        nextUserId: findNextUserId(self, userId, capabilities),
        userId,
        modifierType: import_cc.UserCredentialModifierType.ZWave,
        modifierNodeId: controller.ownNodeId,
        userType: import_cc.UserCredentialUserType.General,
        active: false,
        credentialRule: import_cc.UserCredentialRule.Single,
        expiringTimeoutMinutes: 0,
        nameEncoding: import_cc.UserCredentialNameEncoding.ASCII,
        userName: ""
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "ok" };
    }
    if (userId === 0) {
      return { action: "fail" };
    }
    if (userId > capabilities.numberOfSupportedUsers) {
      return { action: "fail" };
    }
    const userType = setCC.userType ?? import_cc.UserCredentialUserType.General;
    if (!(capabilities.supportedUserTypes ?? []).includes(userType)) {
      return { action: "fail" };
    }
    const credentialRule = setCC.credentialRule ?? import_cc.UserCredentialRule.Single;
    if (!capabilities.supportedCredentialRules.includes(credentialRule)) {
      return { action: "fail" };
    }
    const nameEncoding = setCC.nameEncoding ?? import_cc.UserCredentialNameEncoding.ASCII;
    if (!(capabilities.supportedUserNameEncodings ?? []).includes(nameEncoding)) {
      return { action: "fail" };
    }
    const existingUser = getUser(self, userId);
    if (operationType === import_cc.UserCredentialOperationType.Add) {
      if (existingUser) {
        const report2 = new import_UserCredentialCC.UserCredentialCCUserReport({
          nodeId: controller.ownNodeId,
          reportType: import_cc.UserCredentialUserReportType.UserAddRejectedLocationOccupied,
          nextUserId: findNextUserId(self, userId, capabilities),
          userId,
          modifierType: existingUser.modifierType,
          modifierNodeId: existingUser.modifierNodeId,
          userType: existingUser.userType,
          active: existingUser.active,
          credentialRule: existingUser.credentialRule,
          expiringTimeoutMinutes: existingUser.expiringTimeoutMinutes,
          nameEncoding: existingUser.nameEncoding,
          userName: existingUser.userName
        });
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "fail" };
      }
    } else if (operationType === import_cc.UserCredentialOperationType.Modify) {
      if (!existingUser) {
        const report2 = new import_UserCredentialCC.UserCredentialCCUserReport({
          nodeId: controller.ownNodeId,
          reportType: import_cc.UserCredentialUserReportType.UserModifyRejectedLocationEmpty,
          nextUserId: findNextUserId(self, userId, capabilities),
          userId,
          modifierType: import_cc.UserCredentialModifierType.DoesNotExist,
          modifierNodeId: 0,
          userType: import_cc.UserCredentialUserType.General,
          active: false,
          credentialRule: import_cc.UserCredentialRule.Single,
          expiringTimeoutMinutes: 0,
          nameEncoding: import_cc.UserCredentialNameEncoding.ASCII,
          userName: ""
        });
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "fail" };
      }
    }
    const active = setCC.active ?? true;
    let expiringTimeoutMinutes = setCC.expiringTimeoutMinutes ?? 0;
    if (userType === import_cc.UserCredentialUserType.Expiring && expiringTimeoutMinutes === 0) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserReport({
        nodeId: controller.ownNodeId,
        reportType: import_cc.UserCredentialUserReportType.ZeroExpiringMinutesInvalid,
        nextUserId: findNextUserId(self, userId, capabilities),
        userId,
        modifierType: existingUser?.modifierType ?? import_cc.UserCredentialModifierType.DoesNotExist,
        modifierNodeId: existingUser?.modifierNodeId ?? 0,
        userType: existingUser?.userType ?? import_cc.UserCredentialUserType.General,
        active: existingUser?.active ?? false,
        credentialRule: existingUser?.credentialRule ?? import_cc.UserCredentialRule.Single,
        expiringTimeoutMinutes: existingUser?.expiringTimeoutMinutes ?? 0,
        nameEncoding: existingUser?.nameEncoding ?? import_cc.UserCredentialNameEncoding.ASCII,
        userName: existingUser?.userName ?? ""
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    if (userType !== import_cc.UserCredentialUserType.Expiring) {
      expiringTimeoutMinutes = 0;
    }
    let userName = setCC.userName ?? "";
    if (userName.length > capabilities.maxUserNameLength) {
      userName = userName.slice(0, capabilities.maxUserNameLength);
    }
    if (userName.length === 0) {
      userName = `User-${userId}`;
    }
    const newUser = {
      userType,
      active,
      credentialRule,
      expiringTimeoutMinutes,
      nameEncoding,
      userName,
      // CC:0083.01.07.11.008: When User Modifier Type is Z-Wave,
      // Modifier Node ID MUST be the sending node's ID.
      modifierType: import_cc.UserCredentialModifierType.ZWave,
      modifierNodeId: controller.ownNodeId
    };
    if (existingUser) {
      if (existingUser.userType === newUser.userType && existingUser.active === newUser.active && existingUser.credentialRule === newUser.credentialRule && existingUser.expiringTimeoutMinutes === newUser.expiringTimeoutMinutes && existingUser.nameEncoding === newUser.nameEncoding && existingUser.userName === newUser.userName) {
        const report2 = new import_UserCredentialCC.UserCredentialCCUserReport({
          nodeId: controller.ownNodeId,
          reportType: import_cc.UserCredentialUserReportType.UserUnchanged,
          nextUserId: findNextUserId(self, userId, capabilities),
          userId,
          modifierType: existingUser.modifierType,
          modifierNodeId: existingUser.modifierNodeId,
          userType: existingUser.userType,
          active: existingUser.active,
          credentialRule: existingUser.credentialRule,
          expiringTimeoutMinutes: existingUser.expiringTimeoutMinutes,
          nameEncoding: existingUser.nameEncoding,
          userName: existingUser.userName
        });
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "ok" };
      }
    }
    setUser(self, userId, newUser);
    const reportType = operationType === import_cc.UserCredentialOperationType.Add ? import_cc.UserCredentialUserReportType.UserAdded : import_cc.UserCredentialUserReportType.UserModified;
    const report = new import_UserCredentialCC.UserCredentialCCUserReport({
      nodeId: controller.ownNodeId,
      reportType,
      nextUserId: findNextUserId(self, userId, capabilities),
      userId,
      modifierType: newUser.modifierType,
      modifierNodeId: newUser.modifierNodeId,
      userType: newUser.userType,
      active: newUser.active,
      credentialRule: newUser.credentialRule,
      expiringTimeoutMinutes: newUser.expiringTimeoutMinutes,
      nameEncoding: newUser.nameEncoding,
      userName: newUser.userName
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToCredentialGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      const reqUserId = receivedCC.userId;
      const reqType = receivedCC.credentialType;
      const reqSlot = receivedCC.credentialSlot;
      const getCRB = /* @__PURE__ */ __name((credentialType) => {
        const typeCaps = capabilities.supportedCredentialTypes.get(credentialType);
        if (!typeCaps)
          return false;
        return typeCaps.maxCredentialHashLength === 0;
      }, "getCRB");
      const emptyReport = /* @__PURE__ */ __name((userId, credentialType, credentialSlot, nextType, nextSlot) => new import_UserCredentialCC.UserCredentialCCCredentialReport({
        nodeId: controller.ownNodeId,
        reportType: import_cc.UserCredentialCredentialReportType.ResponseToGet,
        userId,
        credentialType,
        credentialSlot,
        credentialReadBack: getCRB(credentialType),
        credentialLength: 0,
        credentialData: new import_shared.Bytes(),
        modifierType: import_cc.UserCredentialModifierType.DoesNotExist,
        modifierNodeId: 0,
        nextCredentialType: nextType,
        nextCredentialSlot: nextSlot
      }), "emptyReport");
      const populatedReport = /* @__PURE__ */ __name((ref, credState, nextType, nextSlot) => new import_UserCredentialCC.UserCredentialCCCredentialReport({
        nodeId: controller.ownNodeId,
        reportType: import_cc.UserCredentialCredentialReportType.ResponseToGet,
        // CC:0083.01.0C.11.010: UUID MUST be set to the User
        // associated with this credential.
        userId: ref.userId,
        credentialType: ref.credentialType,
        credentialSlot: ref.credentialSlot,
        credentialReadBack: getCRB(ref.credentialType),
        credentialLength: credState.credentialData.length,
        credentialData: credState.credentialData,
        modifierType: credState.modifierType,
        modifierNodeId: credState.modifierNodeId,
        nextCredentialType: nextType,
        nextCredentialSlot: nextSlot
      }), "populatedReport");
      const findFirst = /* @__PURE__ */ __name((filter) => {
        return getAllCredentials(self, capabilities).find(filter);
      }, "findFirst");
      const scopedUserId = reqUserId !== 0 ? reqUserId : void 0;
      if (reqUserId !== 0 && reqType !== 0 && reqSlot !== 0) {
        const cred = getCredential(self, reqUserId, reqType, reqSlot);
        const next = findNextCredential(self, reqType, reqSlot, capabilities, scopedUserId);
        if (!cred) {
          return {
            action: "sendCC",
            cc: emptyReport(reqUserId, reqType, reqSlot, next.nextCredentialType, next.nextCredentialSlot)
          };
        }
        return {
          action: "sendCC",
          cc: populatedReport({
            userId: reqUserId,
            credentialType: reqType,
            credentialSlot: reqSlot
          }, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      } else if (reqUserId === 0 && reqType !== 0 && reqSlot !== 0) {
        const ref = findFirst((r) => r.credentialType === reqType && r.credentialSlot === reqSlot);
        const next = findNextCredential(self, reqType, reqSlot, capabilities, scopedUserId);
        if (!ref) {
          return {
            action: "sendCC",
            cc: emptyReport(0, reqType, reqSlot, next.nextCredentialType, next.nextCredentialSlot)
          };
        }
        const cred = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
        return {
          action: "sendCC",
          cc: populatedReport(ref, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      } else if (reqUserId !== 0 && reqType !== 0 && reqSlot === 0) {
        const ref = findFirst((r) => r.userId === reqUserId && r.credentialType === reqType);
        if (!ref) {
          const userExists = getUser(self, reqUserId) !== void 0;
          const next2 = userExists ? findNextCredential(self, reqType, 0, capabilities, scopedUserId) : {
            nextCredentialType: import_cc.UserCredentialType.None,
            nextCredentialSlot: 0
          };
          return {
            action: "sendCC",
            cc: emptyReport(reqUserId, reqType, 0, next2.nextCredentialType, next2.nextCredentialSlot)
          };
        }
        const cred = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
        const next = findNextCredential(self, ref.credentialType, ref.credentialSlot, capabilities, scopedUserId);
        return {
          action: "sendCC",
          cc: populatedReport(ref, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      } else if (reqUserId !== 0 && reqType === 0 && reqSlot === 0) {
        const ref = findFirst((r) => r.userId === reqUserId);
        if (!ref) {
          const userExists = getUser(self, reqUserId) !== void 0;
          const next2 = userExists ? findNextCredential(self, import_cc.UserCredentialType.None, 0, capabilities, scopedUserId) : {
            nextCredentialType: import_cc.UserCredentialType.None,
            nextCredentialSlot: 0
          };
          return {
            action: "sendCC",
            cc: emptyReport(reqUserId, import_cc.UserCredentialType.None, 0, next2.nextCredentialType, next2.nextCredentialSlot)
          };
        }
        const cred = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
        const next = findNextCredential(self, ref.credentialType, ref.credentialSlot, capabilities, scopedUserId);
        return {
          action: "sendCC",
          cc: populatedReport(ref, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      } else if (reqUserId === 0 && reqType !== 0 && reqSlot === 0) {
        const ref = findFirst((r) => r.credentialType === reqType);
        if (!ref) {
          const next2 = findNextCredential(self, reqType, 0, capabilities, scopedUserId);
          return {
            action: "sendCC",
            cc: emptyReport(0, reqType, 0, next2.nextCredentialType, next2.nextCredentialSlot)
          };
        }
        const cred = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
        const next = findNextCredential(self, ref.credentialType, ref.credentialSlot, capabilities, scopedUserId);
        return {
          action: "sendCC",
          cc: populatedReport(ref, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      } else if (reqUserId === 0 && reqType === 0 && reqSlot === 0) {
        const ref = findFirst(() => true);
        if (!ref) {
          return {
            action: "sendCC",
            cc: emptyReport(0, import_cc.UserCredentialType.None, 0, import_cc.UserCredentialType.None, 0)
          };
        }
        const cred = getCredential(self, ref.userId, ref.credentialType, ref.credentialSlot);
        const next = findNextCredential(self, ref.credentialType, ref.credentialSlot, capabilities, scopedUserId);
        return {
          action: "sendCC",
          cc: populatedReport(ref, cred, next.nextCredentialType, next.nextCredentialSlot)
        };
      }
    }
  }
};
const respondToCredentialSet = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialSet))
      return;
    const setCC = receivedCC;
    const capabilities = getCapabilities(self, setCC.endpointIndex);
    const userId = setCC.userId;
    const credentialType = setCC.credentialType;
    const credentialSlot = setCC.credentialSlot;
    const operationType = setCC.operationType;
    const credentialData = setCC.credentialData ?? new import_shared.Bytes();
    const typeCaps = capabilities.supportedCredentialTypes.get(credentialType);
    const getCRB = /* @__PURE__ */ __name((type) => {
      const tc = capabilities.supportedCredentialTypes.get(type);
      return tc ? tc.maxCredentialHashLength === 0 : false;
    }, "getCRB");
    const makeReport = /* @__PURE__ */ __name((reportType2, rUserId, rType, rSlot, rData, rModType, rModNode) => new import_UserCredentialCC.UserCredentialCCCredentialReport({
      nodeId: controller.ownNodeId,
      reportType: reportType2,
      userId: rUserId,
      credentialType: rType,
      credentialSlot: rSlot,
      credentialReadBack: getCRB(rType),
      credentialLength: rData.length,
      credentialData: rData,
      modifierType: rModType,
      modifierNodeId: rModNode,
      nextCredentialType: import_cc.UserCredentialType.None,
      nextCredentialSlot: 0
    }), "makeReport");
    if (operationType === import_cc.UserCredentialOperationType.Delete) {
      if (userId === 0 && credentialType === 0 && credentialSlot === 0) {
        deleteAllCredentials(self, capabilities);
      } else if (userId === 0 && credentialType !== 0 && credentialSlot === 0) {
        deleteCredentialsForType(self, credentialType, capabilities);
      } else if (userId !== 0 && credentialType === 0 && credentialSlot === 0) {
        deleteCredentialsForUser(self, userId, capabilities);
      } else if (userId !== 0 && credentialType !== 0 && credentialSlot === 0) {
        deleteCredentialsForUserAndType(self, userId, credentialType, capabilities);
      } else if (userId !== 0 && credentialType !== 0 && credentialSlot !== 0) {
        deleteCredential(self, userId, credentialType, credentialSlot);
      } else {
        return { action: "fail" };
      }
      const report2 = makeReport(import_cc.UserCredentialCredentialReportType.CredentialDeleted, userId, credentialType, credentialSlot, new import_shared.Bytes(), import_cc.UserCredentialModifierType.ZWave, controller.ownNodeId);
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "ok" };
    }
    if (!typeCaps) {
      return { action: "fail" };
    }
    if (credentialSlot === 0) {
      return { action: "fail" };
    }
    if (credentialSlot > typeCaps.numberOfCredentialSlots) {
      return { action: "fail" };
    }
    if (typeCaps.minCredentialLength === 0 && typeCaps.maxCredentialLength === 0) {
      return { action: "fail" };
    }
    if (credentialData.length > typeCaps.maxCredentialLength) {
      return { action: "fail" };
    }
    if (credentialData.length < typeCaps.minCredentialLength) {
      return { action: "fail" };
    }
    const existingCred = getCredential(self, userId, credentialType, credentialSlot);
    const user = getUser(self, userId);
    if (!user) {
      const report2 = makeReport(import_cc.UserCredentialCredentialReportType.WrongUserUniqueIdentifier, userId, credentialType, credentialSlot, new import_shared.Bytes(), import_cc.UserCredentialModifierType.DoesNotExist, 0);
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    if (operationType === import_cc.UserCredentialOperationType.Add) {
      if (existingCred) {
        const report2 = makeReport(import_cc.UserCredentialCredentialReportType.CredentialAddRejectedLocationOccupied, userId, credentialType, credentialSlot, existingCred.credentialData, existingCred.modifierType, existingCred.modifierNodeId);
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "fail" };
      }
    } else if (operationType === import_cc.UserCredentialOperationType.Modify) {
      if (!existingCred) {
        const report2 = makeReport(import_cc.UserCredentialCredentialReportType.CredentialModifyRejectedLocationEmpty, userId, credentialType, credentialSlot, new import_shared.Bytes(), import_cc.UserCredentialModifierType.DoesNotExist, 0);
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "fail" };
      }
    }
    if (isDuplicateCredential(self, credentialType, credentialData, capabilities, userId, credentialSlot)) {
      const report2 = makeReport(import_cc.UserCredentialCredentialReportType.DuplicateCredential, userId, credentialType, credentialSlot, existingCred?.credentialData ?? new import_shared.Bytes(), existingCred?.modifierType ?? import_cc.UserCredentialModifierType.DoesNotExist, existingCred?.modifierNodeId ?? 0);
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    if (credentialType === import_cc.UserCredentialType.PINCode && isDuplicateOfAdminCode(self, credentialData)) {
      const report2 = makeReport(import_cc.UserCredentialCredentialReportType.DuplicateAdminPINCode, userId, credentialType, credentialSlot, existingCred?.credentialData ?? new import_shared.Bytes(), existingCred?.modifierType ?? import_cc.UserCredentialModifierType.DoesNotExist, existingCred?.modifierNodeId ?? 0);
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    if (existingCred) {
      if (existingCred.credentialData.length === credentialData.length && existingCred.credentialData.every((b, i) => b === credentialData[i])) {
        const report2 = makeReport(import_cc.UserCredentialCredentialReportType.CredentialUnchanged, userId, credentialType, credentialSlot, existingCred.credentialData, existingCred.modifierType, existingCred.modifierNodeId);
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "ok" };
      }
    }
    const newCred = {
      credentialData,
      // CC:0083.01.0C.11.030: When Modifier Type is Z-Wave, Node ID
      // MUST be the sending node's ID.
      modifierType: import_cc.UserCredentialModifierType.ZWave,
      modifierNodeId: controller.ownNodeId
    };
    setCredential(self, userId, credentialType, credentialSlot, newCred);
    const reportType = operationType === import_cc.UserCredentialOperationType.Add ? import_cc.UserCredentialCredentialReportType.CredentialAdded : import_cc.UserCredentialCredentialReportType.CredentialModified;
    const report = makeReport(reportType, userId, credentialType, credentialSlot, credentialData, newCred.modifierType, newCred.modifierNodeId);
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToAdminPinCodeGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCAdminPinCodeGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      if (!capabilities.supportsAdminCode) {
        return { action: "stop" };
      }
      const adminCode = self.state.get(StateKeys.adminPinCode) ?? "";
      const cc = new import_UserCredentialCC.UserCredentialCCAdminPinCodeReport({
        nodeId: controller.ownNodeId,
        operationResult: import_cc.UserCredentialAdminCodeOperationResult.ResponseToGet,
        // CC:0083.01.1C.11.005: Length MUST be 0 if deactivated
        pinCode: adminCode
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToAdminPinCodeSet = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCAdminPinCodeSet))
      return;
    const setCC = receivedCC;
    const capabilities = getCapabilities(self, setCC.endpointIndex);
    if (!capabilities.supportsAdminCode) {
      return { action: "fail" };
    }
    const pinCode = setCC.pinCode;
    if (pinCode.length === 0) {
      if (capabilities.supportsAdminCodeDeactivation) {
        self.state.set(StateKeys.adminPinCode, "");
        const report2 = new import_UserCredentialCC.UserCredentialCCAdminPinCodeReport({
          nodeId: controller.ownNodeId,
          operationResult: import_cc.UserCredentialAdminCodeOperationResult.Modified,
          pinCode: ""
        });
        await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
          ackRequested: false
        }));
        return { action: "ok" };
      }
      return { action: "fail" };
    }
    const pinCaps = capabilities.supportedCredentialTypes.get(import_cc.UserCredentialType.PINCode);
    if (pinCaps) {
      if (pinCode.length < pinCaps.minCredentialLength || pinCode.length > pinCaps.maxCredentialLength) {
        return { action: "fail" };
      }
    }
    const currentCode = self.state.get(StateKeys.adminPinCode) ?? "";
    if (pinCode === currentCode) {
      const report2 = new import_UserCredentialCC.UserCredentialCCAdminPinCodeReport({
        nodeId: controller.ownNodeId,
        operationResult: import_cc.UserCredentialAdminCodeOperationResult.Unmodified,
        pinCode: currentCode
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "ok" };
    }
    const pinBytes = import_shared.Bytes.from(pinCode, "ascii");
    if (isDuplicateCredential(self, import_cc.UserCredentialType.PINCode, pinBytes, capabilities)) {
      const report2 = new import_UserCredentialCC.UserCredentialCCAdminPinCodeReport({
        nodeId: controller.ownNodeId,
        operationResult: import_cc.UserCredentialAdminCodeOperationResult.FailDuplicateCredential,
        pinCode: currentCode
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    self.state.set(StateKeys.adminPinCode, pinCode);
    const report = new import_UserCredentialCC.UserCredentialCCAdminPinCodeReport({
      nodeId: controller.ownNodeId,
      operationResult: import_cc.UserCredentialAdminCodeOperationResult.Modified,
      pinCode
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToAllUsersChecksumGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCAllUsersChecksumGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      if (!capabilities.supportsAllUsersChecksum) {
        return { action: "stop" };
      }
      const allUserIds = getAllUserIds(self, capabilities);
      if (allUserIds.length === 0) {
        const cc2 = new import_UserCredentialCC.UserCredentialCCAllUsersChecksumReport({
          nodeId: controller.ownNodeId,
          checksum: 0
        });
        return { action: "sendCC", cc: cc2 };
      }
      let checksumData = new import_shared.Bytes();
      for (const userId of allUserIds) {
        const uuidBuf = new import_shared.Bytes(2);
        uuidBuf.writeUInt16BE(userId, 0);
        const userData = buildUserChecksumData(self, userId, capabilities);
        checksumData = import_shared.Bytes.concat([
          checksumData,
          uuidBuf,
          userData
        ]);
      }
      const checksum = checksumData.length > 0 ? (0, import_core.CRC16_CCITT)(checksumData) : 0;
      const cc = new import_UserCredentialCC.UserCredentialCCAllUsersChecksumReport({
        nodeId: controller.ownNodeId,
        checksum
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToUserChecksumGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCUserChecksumGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      if (!capabilities.supportsUserChecksum) {
        return { action: "stop" };
      }
      const userId = receivedCC.userId;
      const user = getUser(self, userId);
      if (!user) {
        const cc2 = new import_UserCredentialCC.UserCredentialCCUserChecksumReport({
          nodeId: controller.ownNodeId,
          userId,
          checksum: 0
        });
        return { action: "sendCC", cc: cc2 };
      }
      const data = buildUserChecksumData(self, userId, capabilities);
      const checksum = data.length > 0 ? (0, import_core.CRC16_CCITT)(data) : 0;
      const cc = new import_UserCredentialCC.UserCredentialCCUserChecksumReport({
        nodeId: controller.ownNodeId,
        userId,
        checksum
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToCredentialChecksumGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialChecksumGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      if (!capabilities.supportsCredentialChecksum) {
        return { action: "stop" };
      }
      const credentialType = receivedCC.credentialType;
      const data = buildCredentialChecksumData(self, credentialType, capabilities);
      const checksum = data.length > 0 ? (0, import_core.CRC16_CCITT)(data) : 0;
      const cc = new import_UserCredentialCC.UserCredentialCCCredentialChecksumReport({
        nodeId: controller.ownNodeId,
        credentialType,
        checksum
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToUserCredentialAssociationSet = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCUserCredentialAssociationSet))
      return;
    const setCC = receivedCC;
    const capabilities = getCapabilities(self, setCC.endpointIndex);
    const credentialType = setCC.credentialType;
    const credentialSlot = setCC.credentialSlot;
    const destinationUserId = setCC.destinationUserId;
    if (!capabilities.supportedCredentialTypes.has(credentialType)) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
        nodeId: controller.ownNodeId,
        credentialType,
        credentialSlot,
        destinationUserId,
        status: 1
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    const typeCaps = capabilities.supportedCredentialTypes.get(credentialType);
    if (credentialSlot === 0 || credentialSlot > typeCaps.numberOfCredentialSlots) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
        nodeId: controller.ownNodeId,
        credentialType,
        credentialSlot,
        destinationUserId,
        status: 2
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    const allCreds = getAllCredentials(self, capabilities);
    const credRef = allCreds.find((c) => c.credentialType === credentialType && c.credentialSlot === credentialSlot);
    if (!credRef) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
        nodeId: controller.ownNodeId,
        credentialType,
        credentialSlot,
        destinationUserId,
        status: 3
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    if (destinationUserId === 0 || destinationUserId > capabilities.numberOfSupportedUsers) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
        nodeId: controller.ownNodeId,
        credentialType,
        credentialSlot,
        destinationUserId,
        status: 4
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    const destUser = getUser(self, destinationUserId);
    if (!destUser) {
      const report2 = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
        nodeId: controller.ownNodeId,
        credentialType,
        credentialSlot,
        destinationUserId,
        status: 5
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "fail" };
    }
    const credState = getCredential(self, credRef.userId, credentialType, credentialSlot);
    deleteCredential(self, credRef.userId, credentialType, credentialSlot);
    setCredential(self, destinationUserId, credentialType, credentialSlot, {
      credentialData: credState.credentialData,
      modifierType: import_cc.UserCredentialModifierType.ZWave,
      modifierNodeId: controller.ownNodeId
    });
    const report = new import_UserCredentialCC.UserCredentialCCUserCredentialAssociationReport({
      nodeId: controller.ownNodeId,
      credentialType,
      credentialSlot,
      destinationUserId,
      status: 0
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToCredentialLearnStart = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialLearnStart)) {
      return;
    }
    const startCC = receivedCC;
    const capabilities = getCapabilities(self, startCC.endpointIndex);
    if (!capabilities.supportedCredentialTypes.has(startCC.credentialType)) {
      return { action: "fail" };
    }
    const typeCaps = capabilities.supportedCredentialTypes.get(startCC.credentialType);
    if (!typeCaps.supportsCredentialLearn) {
      return { action: "fail" };
    }
    if (startCC.userId === 0 || startCC.userId > capabilities.numberOfSupportedUsers) {
      return { action: "fail" };
    }
    if (startCC.credentialSlot === 0 || startCC.credentialSlot > typeCaps.numberOfCredentialSlots) {
      return { action: "fail" };
    }
    const report = new import_UserCredentialCC.UserCredentialCCCredentialLearnReport({
      nodeId: controller.ownNodeId,
      learnStatus: 4,
      // Timeout
      userId: startCC.userId,
      credentialType: startCC.credentialType,
      credentialSlot: startCC.credentialSlot,
      // CC:0083.01.11.11.003: Steps Remaining MUST be 0 for Timeout
      stepsRemaining: 0
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToCredentialLearnCancel = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCCredentialLearnCancel)) {
      return;
    }
    const report = new import_UserCredentialCC.UserCredentialCCCredentialLearnReport({
      nodeId: controller.ownNodeId,
      learnStatus: 3,
      // Ended Not Due to Timeout
      userId: 0,
      credentialType: import_cc.UserCredentialType.None,
      credentialSlot: 0,
      stepsRemaining: 0
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const respondToKeyLockerEntryGet = {
  handleCC(controller, self, receivedCC) {
    if (receivedCC instanceof import_UserCredentialCC.UserCredentialCCKeyLockerEntryGet) {
      const capabilities = getCapabilities(self, receivedCC.endpointIndex);
      if (!capabilities.supportedKeyLockerEntryTypes || capabilities.supportedKeyLockerEntryTypes.size === 0) {
        return { action: "stop" };
      }
      const entryType = receivedCC.entryType;
      const entrySlot = receivedCC.entrySlot;
      const entryData = self.state.get(StateKeys.keyLockerEntry(entryType, entrySlot));
      const cc = new import_UserCredentialCC.UserCredentialCCKeyLockerEntryReport({
        nodeId: controller.ownNodeId,
        occupied: entryData !== void 0 && entryData.length > 0,
        entryType,
        entrySlot
      });
      return { action: "sendCC", cc };
    }
  }
};
const respondToKeyLockerEntrySet = {
  async handleCC(controller, self, receivedCC) {
    if (!(receivedCC instanceof import_UserCredentialCC.UserCredentialCCKeyLockerEntrySet))
      return;
    const setCC = receivedCC;
    const capabilities = getCapabilities(self, setCC.endpointIndex);
    if (!capabilities.supportedKeyLockerEntryTypes || capabilities.supportedKeyLockerEntryTypes.size === 0) {
      return { action: "fail" };
    }
    const entryType = setCC.entryType;
    const entrySlot = setCC.entrySlot;
    const operationType = setCC.operationType;
    const entryData = setCC.entryData ?? new import_shared.Bytes();
    if (operationType === import_cc.UserCredentialOperationType.Delete) {
      if (entryType === 0 && entrySlot > 0) {
        return { action: "fail" };
      }
      if (entryType === 0 && entrySlot === 0) {
        for (const [type, typeCaps2] of capabilities.supportedKeyLockerEntryTypes) {
          for (let slot = 1; slot <= typeCaps2.numberOfEntrySlots; slot++) {
            self.state.delete(StateKeys.keyLockerEntry(type, slot));
          }
        }
      } else if (entryType !== 0 && entrySlot === 0) {
        const typeCaps2 = capabilities.supportedKeyLockerEntryTypes.get(entryType);
        if (typeCaps2) {
          for (let slot = 1; slot <= typeCaps2.numberOfEntrySlots; slot++) {
            self.state.delete(StateKeys.keyLockerEntry(entryType, slot));
          }
        }
      } else {
        self.state.delete(StateKeys.keyLockerEntry(entryType, entrySlot));
      }
      const report2 = new import_UserCredentialCC.UserCredentialCCKeyLockerEntryReport({
        nodeId: controller.ownNodeId,
        occupied: false,
        entryType: entryType || import_cc.UserCredentialKeyLockerEntryType.DESFireApplicationIdAndKey,
        entrySlot: entrySlot || 1
      });
      await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report2, {
        ackRequested: false
      }));
      return { action: "ok" };
    }
    const typeCaps = capabilities.supportedKeyLockerEntryTypes.get(entryType);
    if (!typeCaps) {
      return { action: "fail" };
    }
    if (entrySlot === 0) {
      return { action: "fail" };
    }
    if (entrySlot > typeCaps.numberOfEntrySlots) {
      return { action: "fail" };
    }
    if (entryData.length > typeCaps.maxEntryDataLength) {
      return { action: "fail" };
    }
    if (entryData.length < typeCaps.minEntryDataLength) {
      return { action: "fail" };
    }
    const existingEntry = self.state.get(StateKeys.keyLockerEntry(entryType, entrySlot));
    const isOccupied = existingEntry !== void 0 && existingEntry.length > 0;
    if (operationType === import_cc.UserCredentialOperationType.Add) {
      if (isOccupied) {
        return { action: "fail" };
      }
    } else if (operationType === import_cc.UserCredentialOperationType.Modify) {
      if (!isOccupied) {
        return { action: "fail" };
      }
    }
    self.state.set(StateKeys.keyLockerEntry(entryType, entrySlot), entryData);
    const report = new import_UserCredentialCC.UserCredentialCCKeyLockerEntryReport({
      nodeId: controller.ownNodeId,
      occupied: true,
      entryType,
      entrySlot
    });
    await self.sendToController((0, import_testing.createMockZWaveRequestFrame)(report, {
      ackRequested: false
    }));
    return { action: "ok" };
  }
};
const UserCredentialCCBehaviors = [
  respondToUserCapabilitiesGet,
  respondToCredentialCapabilitiesGet,
  respondToKeyLockerCapabilitiesGet,
  respondToUserGet,
  respondToUserSet,
  respondToCredentialGet,
  respondToCredentialSet,
  respondToAdminPinCodeGet,
  respondToAdminPinCodeSet,
  respondToAllUsersChecksumGet,
  respondToUserChecksumGet,
  respondToCredentialChecksumGet,
  respondToUserCredentialAssociationSet,
  respondToCredentialLearnStart,
  respondToCredentialLearnCancel,
  respondToKeyLockerEntryGet,
  respondToKeyLockerEntrySet
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  UserCredentialCCBehaviors,
  defaultCapabilities
});
//# sourceMappingURL=UserCredential.js.map
