export const LOCKED_PERMISSION_USER_TYPE_IDS = Object.freeze([1, 7]);

export const LOCKED_PERMISSION_USER_TYPE_DESCRIPTIONS = Object.freeze(["admin", "manager"]);

export const PERMISSION_FLAGS = Object.freeze([
  { key: "read",   labelKey: "settings.permissions.read",   bit: 1 },
  { key: "create", labelKey: "settings.permissions.create", bit: 2 },
  { key: "edit",   labelKey: "settings.permissions.edit",   bit: 4 },
  { key: "delete", labelKey: "settings.permissions.delete", bit: 8 },
  { key: "print",  labelKey: "settings.permissions.print",  bit: 16 },
  { key: "export", labelKey: "settings.permissions.export", bit: 32 },
  { key: "import", labelKey: "settings.permissions.import", bit: 64 },
  { key: "limit",  labelKey: "settings.permissions.limit",  bit: 128 },
  { key: "menu",   labelKey: "settings.permissions.menu",   bit: 256 },
]);

const USER_TYPE_DESCRIPTION_KEY_MAP = Object.freeze({
  admin:        "settings.user_types.types.admin",
  guest:        "settings.user_types.types.guest",
  owner:        "settings.user_types.types.owner",
  receptionist: "settings.user_types.types.receptionist",
  customer:     "settings.user_types.types.customer",
  provider:     "settings.user_types.types.provider",
  manager:      "settings.user_types.types.manager",
  agent:        "settings.user_types.types.agent",
});

const capitalize = (value) => {
  if (!value) {
    return "";
  }

  return value.charAt(0).toUpperCase() + value.slice(1);
};

export const translateUserTypeLabel = (userType, t) => {
  const description =
    typeof userType === "object" && userType !== null
      ? userType.description
      : String(userType || "");

  const translationKey = USER_TYPE_DESCRIPTION_KEY_MAP[description];

  if (translationKey) {
    return t(translationKey);
  }

  return capitalize(description);
};

export const translateUserTypeLabelUppercase = (userType, t) =>
  translateUserTypeLabel(userType, t).toUpperCase();

export const normalizeUserTypeOptions = (payload, t) => {
  const items = Array.isArray(payload) ? payload : [];

  return items
    .map((item) => ({
      label:       translateUserTypeLabel(item, t),
      value:       Number(item.value ?? item.id),
      description: item.description,
      id:          Number(item.id ?? item.value),
    }))
    .sort((a, b) => a.value - b.value);
};

export const isLockedUserTypePermission = (userType) => {
  if (typeof userType === "object" && userType !== null) {
    const description = String(userType.description || "").toLowerCase();

    if (!description) {
      return false;
    }

    return LOCKED_PERMISSION_USER_TYPE_DESCRIPTIONS.includes(description);
  }

  return LOCKED_PERMISSION_USER_TYPE_IDS.includes(Number(userType));
};

const buildOperationNodeKey = (permissionId, flagKey) =>
  `permission-${permissionId}-${flagKey}`;

export const buildPermissionTreeNodes = (
  catalog,
  assignedPermissions = [],
  t,
  readOnly = false,
) => {
  const assignedByPermissionId = new Map(
    assignedPermissions.map((permission) => [permission.permission_id, permission]),
  );

  const permissionsByParentId = catalog.reduce((accumulator, permission) => {
    const parentId = permission.parent_id ?? null;

    if (!accumulator.has(parentId)) {
      accumulator.set(parentId, []);
    }

    accumulator.get(parentId).push(permission);

    return accumulator;
  }, new Map());

  const buildNodes = (parentId = null) => {
    const permissions = permissionsByParentId.get(parentId) || [];

    return permissions.map((permission) => {
      const assignedPermission = assignedByPermissionId.get(permission.id);
      const permissionChildren = buildNodes(permission.id);

      const operationChildren = PERMISSION_FLAGS.filter(
        (flag) => (permission.bits & flag.bit) === flag.bit,
      ).map((flag) => ({
        id:         buildOperationNodeKey(permission.id, flag.key),
        label:      t(flag.labelKey),
        icon:       "mdi-circle-small",
        disabled:   readOnly,
        selectable: false,
      }));

      return {
        id:           `scope-${permission.id}`,
        label:        permission.description,
        caption:      permission.scope,
        icon:         "mdi-shield-key-outline",
        selectable:   false,
        assignedBits: assignedPermission?.bits ?? 0,

        children: [...permissionChildren, ...operationChildren],
      };
    });
  };

  return buildNodes();
};

export const buildTickedKeys = (catalog, assignedPermissions = []) => {
  const catalogById = new Map(catalog.map((permission) => [permission.id, permission]));

  return assignedPermissions.flatMap((permission) => {
    const catalogPermission = catalogById.get(permission.permission_id);

    if (!catalogPermission) {
      return [];
    }

    return PERMISSION_FLAGS.filter(
      (flag) =>
        (catalogPermission.bits & flag.bit) === flag.bit &&
        (permission.bits & flag.bit) === flag.bit,
    ).map((flag) => buildOperationNodeKey(permission.permission_id, flag.key));
  });
};

export const buildPermissionPayloadFromTicked = (tickedKeys) => {
  const permissionsById = new Map();

  tickedKeys.forEach((key) => {
    const match = key.match(/^permission-(\d+)-([a-z_]+)$/);

    if (!match) {
      return;
    }

    const permissionId = Number(match[1]);

    const flagKey = match[2];

    const flag = PERMISSION_FLAGS.find((item) => item.key === flagKey);

    if (!flag) {
      return;
    }

    permissionsById.set(
      permissionId,
      (permissionsById.get(permissionId) || 0) + flag.bit,
    );
  });

  return [...permissionsById.entries()].map(([permission_id, bits]) => ({
    permission_id,
    bits,
  }));
};

export const buildPermissionTooltipLines = (
  catalog,
  assignedPermissions = [],
  t,
) => {
  const catalogById = new Map(catalog.map((permission) => [permission.id, permission]));

  return assignedPermissions.reduce((lines, permission) => {
    const catalogPermission = catalogById.get(permission.permission_id);

    if (!catalogPermission) {
      return lines;
    }

    const labels = PERMISSION_FLAGS.filter(
      (flag) =>
        (catalogPermission.bits & flag.bit) === flag.bit &&
        (permission.bits & flag.bit) === flag.bit,
    ).map((flag) => t(flag.labelKey));

    if (!labels.length) {
      return lines;
    }

    lines.push(`${catalogPermission.description}: ${labels.join(", ")}`);

    return lines;
  }, []);
};
