import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
import { Role, RoleSidebarPreference, SidebarVariant, User, UserSidebarPreference } from "../data/entities.js";
import {
  SIDEBAR_PREFERENCES_VERSION,
  normalizeSidebarSettings
} from "@open-mercato/shared/modules/navigation/sidebarPreferences";
async function loadSidebarPreference(em, scope) {
  const { userId, tenantId, organizationId } = normalizeScope(scope);
  const existing = await findOneWithDecryption(
    em,
    UserSidebarPreference,
    { user: userId, tenantId, organizationId },
    void 0,
    { tenantId, organizationId }
  );
  return normalizeSidebarSettings(existing?.settingsJson);
}
async function loadSidebarPreferenceUpdatedAt(em, scope) {
  const { userId, tenantId, organizationId } = normalizeScope(scope);
  const existing = await findOneWithDecryption(
    em,
    UserSidebarPreference,
    { user: userId, tenantId, organizationId },
    void 0,
    { tenantId, organizationId }
  );
  if (!existing) return null;
  return { id: existing.id, updatedAt: existing.updatedAt ?? null };
}
async function loadRoleSidebarPreferenceUpdatedAt(em, scope) {
  const { roleId, tenantId } = normalizeRoleScope(scope);
  const existing = await findOneWithDecryption(
    em,
    RoleSidebarPreference,
    { role: roleId, tenantId },
    void 0,
    { tenantId, organizationId: null }
  );
  if (!existing) return null;
  return { id: existing.id, updatedAt: existing.updatedAt ?? null };
}
async function saveSidebarPreference(em, scope, input) {
  const normalized = normalizeSidebarSettings({
    ...input,
    version: input?.version ?? SIDEBAR_PREFERENCES_VERSION
  });
  const { userId, tenantId, organizationId, locale } = normalizeScope(scope);
  let pref = await findOneWithDecryption(
    em,
    UserSidebarPreference,
    { user: userId, tenantId, organizationId },
    void 0,
    { tenantId, organizationId }
  );
  if (!pref) {
    pref = em.create(UserSidebarPreference, {
      user: em.getReference(User, userId),
      tenantId,
      organizationId,
      locale,
      settingsJson: normalized,
      createdAt: /* @__PURE__ */ new Date()
    });
  } else {
    pref.settingsJson = normalized;
  }
  await em.flush();
  return normalized;
}
async function loadRoleSidebarPreferences(em, options) {
  if (!options.roleIds.length) return /* @__PURE__ */ new Map();
  const tenantId = options.tenantId ?? null;
  const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] };
  const prefs = await findWithDecryption(
    em,
    RoleSidebarPreference,
    { role: { $in: options.roleIds }, tenantId: tenantFilter },
    void 0,
    { tenantId, organizationId: null }
  );
  const map = /* @__PURE__ */ new Map();
  for (const pref of prefs) {
    const key = pref.role.id;
    if (tenantId !== null) {
      const existing = map.get(key);
      if (existing && pref.tenantId === null) continue;
      if (!existing || pref.tenantId === tenantId) {
        map.set(key, normalizeSidebarSettings(pref.settingsJson));
      }
      continue;
    }
    map.set(key, normalizeSidebarSettings(pref.settingsJson));
  }
  return map;
}
async function loadFirstRoleSidebarPreference(em, options) {
  if (!options.roleIds.length) return null;
  const tenantId = options.tenantId ?? null;
  const tenantFilter = tenantId === null ? null : { $in: [tenantId, null] };
  const prefs = await findWithDecryption(
    em,
    RoleSidebarPreference,
    { role: { $in: options.roleIds }, tenantId: tenantFilter },
    void 0,
    { tenantId, organizationId: null }
  );
  if (!prefs.length) return null;
  const ordered = options.roleIds.map((id) => {
    if (tenantId !== null) {
      const specific = prefs.find((pref) => pref.role.id === id && pref.tenantId === tenantId);
      if (specific) return specific;
    }
    return prefs.find((pref) => pref.role.id === id && pref.tenantId === null);
  }).filter(Boolean);
  const first = ordered[0] ?? prefs[0];
  return normalizeSidebarSettings(first?.settingsJson);
}
async function saveRoleSidebarPreference(em, scope, input) {
  const normalized = normalizeSidebarSettings({
    ...input,
    version: input?.version ?? SIDEBAR_PREFERENCES_VERSION
  });
  const { roleId, tenantId, locale } = normalizeRoleScope(scope);
  let pref = await findOneWithDecryption(
    em,
    RoleSidebarPreference,
    { role: roleId, tenantId },
    void 0,
    { tenantId, organizationId: null }
  );
  if (!pref) {
    pref = em.create(RoleSidebarPreference, {
      role: em.getReference(Role, roleId),
      tenantId,
      locale,
      settingsJson: normalized,
      createdAt: /* @__PURE__ */ new Date()
    });
  } else {
    pref.settingsJson = normalized;
  }
  await em.flush();
  return normalized;
}
function applySidebarPreference(groups, settings) {
  const normalized = normalizeSidebarSettings(settings);
  const orderIndex = /* @__PURE__ */ new Map();
  normalized.groupOrder?.forEach((id, idx) => {
    if (!orderIndex.has(id)) orderIndex.set(id, idx);
  });
  const hiddenSet = new Set(normalized.hiddenItems ?? []);
  const resolveItemKey = (item) => {
    const candidate = item.id?.trim();
    if (candidate && candidate.length > 0) return candidate;
    return item.href;
  };
  const applyItems = (items) => {
    return items.map((item) => {
      const itemKey = resolveItemKey(item);
      const override = normalized.itemLabels?.[itemKey] ?? normalized.itemLabels?.[item.href];
      const nextChildren = item.children ? applyItems(item.children) : void 0;
      const hidden = hiddenSet.has(itemKey) || hiddenSet.has(item.href);
      const next = {
        ...item,
        title: override && override.trim().length > 0 ? override.trim() : item.defaultTitle,
        children: nextChildren
      };
      next.hidden = hidden;
      return next;
    });
  };
  const mapped = groups.map((group) => {
    const override = normalized.groupLabels?.[group.id];
    return {
      ...group,
      name: override && override.trim().length > 0 ? override.trim() : group.defaultName,
      items: applyItems(group.items)
    };
  });
  mapped.sort((a, b) => {
    const ao = orderIndex.has(a.id) ? orderIndex.get(a.id) : Number.POSITIVE_INFINITY;
    const bo = orderIndex.has(b.id) ? orderIndex.get(b.id) : Number.POSITIVE_INFINITY;
    if (ao !== bo) return ao - bo;
    const aw = typeof a.weight === "number" ? a.weight : 1e4;
    const bw = typeof b.weight === "number" ? b.weight : 1e4;
    if (aw !== bw) return aw - bw;
    return a.defaultName.localeCompare(b.defaultName);
  });
  return mapped;
}
function normalizeScope(scope) {
  return {
    userId: scope.userId,
    tenantId: scope.tenantId ?? null,
    organizationId: scope.organizationId ?? null,
    locale: scope.locale
  };
}
function normalizeRoleScope(scope) {
  return {
    roleId: scope.roleId,
    tenantId: scope.tenantId ?? null,
    locale: scope.locale
  };
}
function toVariantRecord(variant) {
  return {
    id: variant.id,
    name: variant.name,
    isActive: variant.isActive === true,
    settings: normalizeSidebarSettings(variant.settingsJson),
    createdAt: variant.createdAt,
    updatedAt: variant.updatedAt ?? null
  };
}
async function listSidebarVariants(em, scope) {
  const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
  const variants = await findWithDecryption(
    em,
    SidebarVariant,
    { user: userId, tenantId, deletedAt: null },
    { orderBy: { createdAt: "asc" } },
    { tenantId, organizationId }
  );
  return variants.map(toVariantRecord);
}
async function loadSidebarVariant(em, scope, variantId) {
  const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
  const variant = await findOneWithDecryption(
    em,
    SidebarVariant,
    { id: variantId, user: userId, tenantId, deletedAt: null },
    void 0,
    { tenantId, organizationId }
  );
  return variant ? toVariantRecord(variant) : null;
}
async function nextVariantAutoName(em, scope, prefix = "My preferences") {
  const variants = await listSidebarVariants(em, scope);
  const usedNumbers = /* @__PURE__ */ new Set();
  for (const variant of variants) {
    if (variant.name === prefix) {
      usedNumbers.add(1);
      continue;
    }
    const escaped = prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
    const match = variant.name.match(new RegExp(`^${escaped}\\s+(\\d+)$`));
    if (match) {
      const n = Number.parseInt(match[1], 10);
      if (!Number.isNaN(n)) usedNumbers.add(n);
    }
  }
  if (!usedNumbers.has(1)) return prefix;
  let next = 2;
  while (usedNumbers.has(next)) next += 1;
  return `${prefix} ${next}`;
}
async function createSidebarVariant(em, scope, input) {
  const { userId, tenantId, organizationId, locale } = normalizeVariantScope(scope);
  const finalName = (input.name ?? "").trim() || await nextVariantAutoName(em, scope);
  const settings = normalizeSidebarSettings({
    ...input.settings ?? {},
    version: input.settings?.version ?? SIDEBAR_PREFERENCES_VERSION
  });
  let variant;
  await withAtomicFlush(em, [
    async () => {
      if (input.isActive === true) {
        await deactivateAllVariants(em, scope);
      }
      variant = em.create(SidebarVariant, {
        user: em.getReference(User, userId),
        tenantId,
        organizationId,
        locale,
        name: finalName,
        settingsJson: settings,
        isActive: input.isActive === true,
        createdAt: /* @__PURE__ */ new Date()
      });
    }
  ], { transaction: true });
  return toVariantRecord(variant);
}
async function updateSidebarVariant(em, scope, variantId, input) {
  const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
  const variant = await findOneWithDecryption(
    em,
    SidebarVariant,
    { id: variantId, user: userId, tenantId, deletedAt: null },
    void 0,
    { tenantId, organizationId }
  );
  if (!variant) return null;
  const target = variant;
  await withAtomicFlush(em, [
    () => {
      if (typeof input.name === "string" && input.name.trim().length > 0) {
        target.name = input.name.trim();
      }
      if (input.settings) {
        target.settingsJson = normalizeSidebarSettings({
          ...input.settings,
          version: input.settings.version ?? SIDEBAR_PREFERENCES_VERSION
        });
      }
      if (typeof input.isActive === "boolean") {
        target.isActive = input.isActive;
      }
    },
    async () => {
      if (input.isActive === true) {
        await deactivateAllVariants(em, scope, variantId);
      }
    }
  ], { transaction: true });
  return toVariantRecord(target);
}
async function deleteSidebarVariant(em, scope, variantId) {
  const { userId, tenantId, organizationId } = normalizeVariantScope(scope);
  const variant = await findOneWithDecryption(
    em,
    SidebarVariant,
    { id: variantId, user: userId, tenantId, deletedAt: null },
    void 0,
    { tenantId, organizationId }
  );
  if (!variant) return false;
  variant.deletedAt = /* @__PURE__ */ new Date();
  variant.isActive = false;
  await em.flush();
  return true;
}
async function deactivateAllVariants(em, scope, exceptId) {
  const { userId, tenantId } = normalizeVariantScope(scope);
  const where = exceptId ? { user: userId, tenantId, isActive: true, deletedAt: null, id: { $ne: exceptId } } : { user: userId, tenantId, isActive: true, deletedAt: null };
  await em.nativeUpdate(SidebarVariant, where, { isActive: false });
}
function normalizeVariantScope(scope) {
  return {
    userId: scope.userId,
    tenantId: scope.tenantId ?? null,
    organizationId: scope.organizationId ?? null,
    locale: scope.locale
  };
}
export {
  applySidebarPreference,
  createSidebarVariant,
  deleteSidebarVariant,
  listSidebarVariants,
  loadFirstRoleSidebarPreference,
  loadRoleSidebarPreferenceUpdatedAt,
  loadRoleSidebarPreferences,
  loadSidebarPreference,
  loadSidebarPreferenceUpdatedAt,
  loadSidebarVariant,
  nextVariantAutoName,
  saveRoleSidebarPreference,
  saveSidebarPreference,
  updateSidebarVariant
};
//# sourceMappingURL=sidebarPreferencesService.js.map
