(() => {
  const PAGE_SIZE = 10;

  const ICONS = {
    user: `<svg class="usersMirIcon" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="7" r="3.5" fill="none" stroke="currentColor" stroke-width="1.4"/><path d="M4 17c0-3.3 2.7-6 6-6s6 2.7 6 6" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/></svg>`,
    edit: `<svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M9.5 2.5l2 2L5 11H3v-2L9.5 2.5z" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/></svg>`,
    delete: `<svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true"><path d="M4 4l6 6M10 4l-6 6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>`,
  };

  const el = (id) => document.getElementById(id);
  const t = (key, vars) => window.I18n?.t(key, vars) ?? key;

  const listEl = el("userList");
  const emptyEl = el("userListEmpty");
  const tableEl = el("usersTable");
  const createBtnEl = el("userCreateBtn");
  const filterInputEl = el("usersFilterInput");
  const filterCountEl = el("usersFilterCount");
  const pageLabelEl = el("usersPageLabel");
  const dialogEl = el("userEditDialog");
  const formEl = el("userEditForm");
  const titleEl = el("userEditTitle");
  const deleteBtnEl = el("userEditDeleteBtn");
  const passwordFieldEl = el("userEditPasswordField");
  const passwordHintEl = el("userEditPasswordHint");
  const pinSectionEl = el("userEditPinSection");
  const pinFieldEl = el("userEditPinField");
  const pinHintEl = el("userEditPinHint");
  const deleteConfirmDialogEl = el("userDeleteConfirmDialog");
  const deleteConfirmTextEl = el("userDeleteConfirmText");

  const fields = {
    displayName: el("userEditDisplayName"),
    username: el("userEditUsername"),
    password: el("userEditPassword"),
    email: el("userEditEmail"),
    group: el("userEditGroup"),
    enabled: el("userEditEnabled"),
    pinEnabled: el("userEditPinEnabled"),
    pin: el("userEditPin"),
  };

  const store = {
    users: [],
    groups: [],
    editingId: null,
    pendingDeleteId: null,
    filter: "",
    page: 1,
  };

  function canWrite() {
    if (!window.AuthApp?.canWrite) return true;
    return window.AuthApp.canWrite("users");
  }

  function currentUserId() {
    return window.AuthApp?.getUser?.()?.id || "";
  }

  function escapeHtml(str) {
    return String(str)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  async function apiJson(url, opts = {}) {
    const res = await fetch(url, { credentials: "include", ...opts });
    const text = await res.text();
    let data = null;
    try {
      data = text ? JSON.parse(text) : null;
    } catch {
      data = null;
    }
    if (!res.ok) throw new Error((data && data.error) || text || res.statusText);
    return data;
  }

  async function refreshAll() {
    const [usersData, groupsData] = await Promise.all([
      apiJson("/api/users"),
      apiJson("/api/user_groups"),
    ]);
    store.users = Array.isArray(usersData.users) ? usersData.users : [];
    store.groups = Array.isArray(groupsData.groups) ? groupsData.groups : [];
  }

  function groupById(id) {
    return store.groups.find((g) => g.id === id) || null;
  }

  function groupAllowsPin(groupId) {
    return !!groupById(groupId)?.allow_pin;
  }

  function filteredUsers() {
    const q = store.filter.trim().toLowerCase();
    let items = [...store.users].sort((a, b) =>
      (a.display_name || a.username || "").localeCompare(b.display_name || b.username || ""),
    );
    if (q) {
      items = items.filter((u) => {
        const name = (u.display_name || "").toLowerCase();
        const username = (u.username || "").toLowerCase();
        const email = (u.email || "").toLowerCase();
        const group = (u.group_name || "").toLowerCase();
        return name.includes(q) || username.includes(q) || email.includes(q) || group.includes(q);
      });
    }
    return items;
  }

  function pageCount(total) {
    return Math.max(1, Math.ceil(total / PAGE_SIZE));
  }

  function pagedItems(items) {
    const totalPages = pageCount(items.length);
    if (store.page > totalPages) store.page = totalPages;
    if (store.page < 1) store.page = 1;
    const start = (store.page - 1) * PAGE_SIZE;
    return items.slice(start, start + PAGE_SIZE);
  }

  function updatePagerUi(totalItems) {
    const totalPages = pageCount(totalItems);
    if (filterCountEl) filterCountEl.textContent = t("users.itemsFound", { n: totalItems });
    if (pageLabelEl) pageLabelEl.textContent = t("users.pageOf", { page: store.page, total: totalPages });
    const atStart = store.page <= 1;
    const atEnd = store.page >= totalPages;
    el("usersPageFirst")?.toggleAttribute("disabled", atStart);
    el("usersPagePrev")?.toggleAttribute("disabled", atStart);
    el("usersPageNext")?.toggleAttribute("disabled", atEnd);
    el("usersPageLast")?.toggleAttribute("disabled", atEnd);
  }

  function pinLabel(user) {
    if (!user.has_pin) return t("users.pinNo");
    return t("users.pinYes");
  }

  function renderList() {
    if (!listEl) return;
    const items = filteredUsers();
    const pageItems = pagedItems(items);
    updatePagerUi(items.length);

    listEl.innerHTML = "";
    const showEmpty = items.length === 0;
    if (tableEl) tableEl.hidden = showEmpty;
    if (emptyEl) {
      emptyEl.hidden = !showEmpty;
      emptyEl.textContent = store.filter.trim() ? t("users.emptyFilter") : t("users.empty");
    }

    pageItems.forEach((user) => {
      const tr = document.createElement("tr");
      tr.className = "mapsMirRow usersMirRow";
      tr.dataset.id = user.id;

      const isSelf = user.id === currentUserId();
      const actions = canWrite()
        ? `<div class="mapsMirRowActions">
            <button type="button" class="mapsMirIconBtn" data-edit="${escapeHtml(user.id)}" title="${escapeHtml(t("common.edit"))}">${ICONS.edit}</button>
            <button type="button" class="mapsMirIconBtn mapsMirIconBtn--danger" data-delete="${escapeHtml(user.id)}" title="${escapeHtml(t("common.delete"))}" ${isSelf ? "disabled" : ""}>${ICONS.delete}</button>
          </div>`
        : "";

      const disabledTag = user.enabled === false ? ` <span class="usersMirDisabledTag">${escapeHtml(t("common.disabled"))}</span>` : "";

      tr.innerHTML = `
        <td class="usersMirCellIcon">${ICONS.user}</td>
        <td class="usersMirCellName">
          <button type="button" class="mapsMirNameLink usersMirNameLink" data-edit="${escapeHtml(user.id)}">${escapeHtml(user.display_name || user.username)}</button>${disabledTag}
        </td>
        <td>${escapeHtml(user.username || "—")}</td>
        <td>${escapeHtml(user.group_name || user.group_id || "—")}</td>
        <td>${escapeHtml(user.email || "—")}</td>
        <td>${escapeHtml(pinLabel(user))}</td>
        <td class="mapsMirCellActions">${actions}</td>`;

      tr.querySelectorAll("[data-edit]").forEach((btn) => {
        btn.addEventListener("click", () => openDialog(btn.dataset.edit));
      });
      tr.querySelector("[data-delete]")?.addEventListener("click", () => openDeleteConfirm(user.id));
      tr.addEventListener("dblclick", () => {
        if (canWrite()) openDialog(user.id);
      });
      listEl.appendChild(tr);
    });

    if (createBtnEl) createBtnEl.hidden = !canWrite();
  }

  function fillGroupSelect(value) {
    if (!fields.group) return;
    fields.group.innerHTML = "";
    store.groups.forEach((g) => {
      const o = document.createElement("option");
      o.value = g.id;
      o.textContent = g.name || g.id;
      if (g.id === value) o.selected = true;
      fields.group.appendChild(o);
    });
  }

  function syncPinUi() {
    const groupId = fields.group?.value || "";
    const allowPin = groupAllowsPin(groupId);
    if (pinSectionEl) pinSectionEl.hidden = !allowPin;
    if (pinHintEl) pinHintEl.hidden = allowPin;
    if (!allowPin) {
      if (fields.pinEnabled) fields.pinEnabled.checked = false;
      if (pinFieldEl) pinFieldEl.hidden = true;
      if (fields.pin) fields.pin.value = "";
      return;
    }
    const pinOn = fields.pinEnabled?.checked;
    if (pinFieldEl) pinFieldEl.hidden = !pinOn;
    if (!pinOn && fields.pin) fields.pin.value = "";
  }

  function openDialog(id = null) {
    store.editingId = id;
    const existing = id ? store.users.find((x) => x.id === id) : null;
    const isEdit = !!existing;

    if (titleEl) titleEl.textContent = isEdit ? t("users.editTitle") : t("users.createTitle");
    if (fields.displayName) fields.displayName.value = existing?.display_name || "";
    if (fields.username) fields.username.value = existing?.username || "";
    if (fields.email) fields.email.value = existing?.email || "";
    if (fields.enabled) fields.enabled.checked = existing?.enabled !== false;
    if (fields.password) fields.password.value = "";
    fillGroupSelect(existing?.group_id || store.groups[0]?.id || "");
    if (fields.pinEnabled) fields.pinEnabled.checked = !!existing?.has_pin;
    if (fields.pin) fields.pin.value = "";

    if (passwordFieldEl) passwordFieldEl.hidden = isEdit;
    if (passwordHintEl) passwordHintEl.hidden = !isEdit;
    if (fields.password) fields.password.required = !isEdit;

    const ro = !canWrite();
    Object.values(fields).forEach((node) => {
      if (!node) return;
      node.disabled = ro;
    });
    if (deleteBtnEl) {
      const isSelf = existing?.id === currentUserId();
      deleteBtnEl.hidden = !isEdit || ro || isSelf;
    }
    syncPinUi();
    dialogEl?.showModal();
  }

  function readPayload(isCreate) {
    const payload = {
      display_name: fields.displayName?.value.trim() || "",
      username: fields.username?.value.trim() || "",
      email: fields.email?.value.trim() || "",
      group_id: fields.group?.value || "",
      enabled: fields.enabled?.checked !== false,
    };
    if (isCreate) payload.password = fields.password?.value || "";
    if (fields.pinEnabled?.checked && groupAllowsPin(payload.group_id)) {
      const pin = (fields.pin?.value || "").trim();
      if (pin) {
        payload.pin = pin;
      } else if (!store.editingId) {
        payload.pin = null;
      }
    } else {
      payload.pin = null;
    }
    return payload;
  }

  async function saveDialog() {
    if (!canWrite()) return;
    const isCreate = !store.editingId;
    const payload = readPayload(isCreate);

    if (!payload.display_name || !payload.username || !payload.group_id) {
      alert(t("users.error.missing"));
      return;
    }
    if (isCreate && !payload.password) {
      alert(t("users.error.passwordRequired"));
      return;
    }
    if (fields.pinEnabled?.checked && groupAllowsPin(payload.group_id)) {
      const pin = (fields.pin?.value || "").trim();
      const needPin = isCreate || !store.users.find((u) => u.id === store.editingId)?.has_pin;
      if (needPin && pin.length !== 4) {
        alert(t("users.error.pinInvalid"));
        return;
      }
    }

    try {
      if (store.editingId) {
        const updatePayload = { ...payload };
        delete updatePayload.password;
        await apiJson(`/api/users/${encodeURIComponent(store.editingId)}`, {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(updatePayload),
        });
      } else {
        await apiJson("/api/users", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
      }
      await refreshAll();
      renderList();
      dialogEl?.close();
    } catch (e) {
      alert(e.message);
    }
  }

  function openDeleteConfirm(id) {
    const user = store.users.find((x) => x.id === id);
    if (!user || !canWrite() || user.id === currentUserId()) return;
    store.pendingDeleteId = id;
    if (deleteConfirmTextEl) {
      deleteConfirmTextEl.textContent = t("users.deleteConfirmText", {
        name: user.display_name || user.username,
      });
    }
    deleteConfirmDialogEl?.showModal();
  }

  function openDeleteConfirmFromDialog() {
    const id = store.editingId;
    if (!id) return;
    openDeleteConfirm(id);
  }

  async function confirmDelete() {
    const id = store.pendingDeleteId || store.editingId;
    if (!id || !canWrite()) return;
    try {
      await apiJson(`/api/users/${encodeURIComponent(id)}`, { method: "DELETE" });
      deleteConfirmDialogEl?.close();
      dialogEl?.close();
      store.editingId = null;
      store.pendingDeleteId = null;
      await refreshAll();
      renderList();
    } catch (e) {
      alert(e.message);
    }
  }

  function clearFilters() {
    store.filter = "";
    store.page = 1;
    if (filterInputEl) filterInputEl.value = "";
    renderList();
  }

  function bindEvents() {
    createBtnEl?.addEventListener("click", () => openDialog(null));
    formEl?.addEventListener("submit", (evt) => {
      evt.preventDefault();
      saveDialog();
    });
    el("userEditCancelBtn")?.addEventListener("click", () => dialogEl?.close());
    dialogEl?.addEventListener("cancel", (evt) => {
      evt.preventDefault();
      dialogEl?.close();
    });
    fields.group?.addEventListener("change", syncPinUi);
    fields.pinEnabled?.addEventListener("change", syncPinUi);
    deleteBtnEl?.addEventListener("click", openDeleteConfirmFromDialog);
    el("userDeleteCancelBtn")?.addEventListener("click", () => {
      store.pendingDeleteId = null;
      deleteConfirmDialogEl?.close();
    });
    el("userDeleteYesBtn")?.addEventListener("click", confirmDelete);
    deleteConfirmDialogEl?.addEventListener("cancel", (evt) => {
      evt.preventDefault();
      store.pendingDeleteId = null;
      deleteConfirmDialogEl?.close();
    });

    filterInputEl?.addEventListener("input", () => {
      store.filter = filterInputEl.value;
      store.page = 1;
      renderList();
    });
    el("usersClearFiltersBtn")?.addEventListener("click", clearFilters);
    el("usersPageFirst")?.addEventListener("click", () => {
      store.page = 1;
      renderList();
    });
    el("usersPagePrev")?.addEventListener("click", () => {
      store.page = Math.max(1, store.page - 1);
      renderList();
    });
    el("usersPageNext")?.addEventListener("click", () => {
      store.page += 1;
      renderList();
    });
    el("usersPageLast")?.addEventListener("click", () => {
      store.page = pageCount(filteredUsers().length);
      renderList();
    });
    el("usersHelpBtn")?.addEventListener("click", () => alert(t("users.helpBody")));

    window.addEventListener("lm:locale-change", () => renderList());
  }

  async function onPageShow() {
    if (!window.AuthApp?.canAccessPage?.("users")) return;
    document.body.classList.toggle("auth-readonly-users", !canWrite());
    try {
      await refreshAll();
      renderList();
    } catch (e) {
      if (emptyEl) {
        emptyEl.hidden = false;
        emptyEl.textContent = e.message;
      }
      if (tableEl) tableEl.hidden = true;
    }
  }

  function onPageHide() {
    dialogEl?.close();
    deleteConfirmDialogEl?.close();
  }

  bindEvents();
  window.UsersApp = { onPageShow, onPageHide };
})();
