(() => {
  const PAGE_SIZE = 10;

  const ICONS = {
    transition: `<svg class="transMirIcon" width="22" height="22" viewBox="0 0 22 22" aria-hidden="true"><circle cx="5" cy="11" r="3.5" fill="none" stroke="currentColor" stroke-width="1.5"/><circle cx="17" cy="11" r="3.5" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M8.5 11h5" stroke="currentColor" stroke-width="1.5" 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("transitionList");
  const emptyEl = el("transitionListEmpty");
  const tableEl = el("transitionsTable");
  const createBtnEl = el("transitionCreateBtn");
  const filterInputEl = el("transitionsFilterInput");
  const filterCountEl = el("transitionsFilterCount");
  const pageLabelEl = el("transitionsPageLabel");
  const dialogEl = el("transitionEditDialog");
  const formEl = el("transitionEditForm");
  const titleEl = el("transitionEditTitle");
  const deleteBtnEl = el("transitionEditDeleteBtn");
  const deleteConfirmDialogEl = el("transitionDeleteConfirmDialog");
  const deleteConfirmTextEl = el("transitionDeleteConfirmText");

  const fields = {
    site: el("transitionEditSite"),
    fromMap: el("transitionEditFromMap"),
    toMap: el("transitionEditToMap"),
    startPos: el("transitionEditStartPos"),
    goalPos: el("transitionEditGoalPos"),
    mission: el("transitionEditMission"),
  };

  const store = {
    transitions: [],
    sites: [],
    maps: [],
    missions: [],
    editingId: null,
    pendingDeleteId: null,
    filter: "",
    page: 1,
  };

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

  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 [sites, maps, missions, transitions] = await Promise.all([
      apiJson("/api/sites"),
      apiJson("/api/maps"),
      apiJson("/api/missions"),
      apiJson("/api/transitions"),
    ]);
    store.sites = Array.isArray(sites.sites) ? sites.sites : [];
    store.maps = Array.isArray(maps.maps) ? maps.maps : [];
    store.missions = Array.isArray(missions.missions) ? missions.missions : [];
    store.transitions = Array.isArray(transitions.transitions) ? transitions.transitions : [];
  }

  function siteName(id) {
    const s = store.sites.find((x) => x.id === id);
    return s?.name || id || "—";
  }

  function mapName(id) {
    const m = store.maps.find((x) => x.id === id);
    return m?.name || id || "—";
  }

  function missionName(id) {
    const m = store.missions.find((x) => x.id === id);
    return m?.name || id || "—";
  }

  function positionName(mapId, positionId) {
    const m = store.maps.find((x) => x.id === mapId);
    const zones = Array.isArray(m?.zones) ? m.zones : [];
    const hit = zones.find((z) => z && z.type === "position" && z.id === positionId);
    return hit?.name || positionId || "—";
  }

  function positionOptionsForMap(mapId) {
    const m = store.maps.find((x) => x.id === mapId);
    const zones = Array.isArray(m?.zones) ? m.zones : [];
    const positions = zones.filter((z) => z && z.type === "position" && typeof z.id === "string");
    return positions.map((p) => ({ value: p.id, label: p.name || p.id }));
  }

  function fillSelect(selectEl, options, value) {
    if (!selectEl) return;
    selectEl.innerHTML = "";
    options.forEach((opt) => {
      const o = document.createElement("option");
      o.value = opt.value;
      o.textContent = opt.label;
      if (opt.value === value) o.selected = true;
      selectEl.appendChild(o);
    });
  }

  function filteredTransitions() {
    const q = store.filter.trim().toLowerCase();
    let items = [...store.transitions].sort((a, b) => {
      const sa = siteName(a.site_id).localeCompare(siteName(b.site_id));
      if (sa !== 0) return sa;
      const startA = positionName(a.from_map_id, a.start_position_id);
      const startB = positionName(b.from_map_id, b.start_position_id);
      return startA.localeCompare(startB);
    });
    if (q) {
      items = items.filter((tr) => {
        const start = positionName(tr.from_map_id, tr.start_position_id).toLowerCase();
        const goal = positionName(tr.to_map_id, tr.goal_position_id).toLowerCase();
        const mission = missionName(tr.mission_id).toLowerCase();
        const fromMap = mapName(tr.from_map_id).toLowerCase();
        const toMap = mapName(tr.to_map_id).toLowerCase();
        const site = siteName(tr.site_id).toLowerCase();
        return (
          start.includes(q) ||
          goal.includes(q) ||
          mission.includes(q) ||
          fromMap.includes(q) ||
          toMap.includes(q) ||
          site.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("transitions.itemsFound", { n: totalItems });
    if (pageLabelEl) pageLabelEl.textContent = t("transitions.pageOf", { page: store.page, total: totalPages });
    const atStart = store.page <= 1;
    const atEnd = store.page >= totalPages;
    el("transitionsPageFirst")?.toggleAttribute("disabled", atStart);
    el("transitionsPagePrev")?.toggleAttribute("disabled", atStart);
    el("transitionsPageNext")?.toggleAttribute("disabled", atEnd);
    el("transitionsPageLast")?.toggleAttribute("disabled", atEnd);
  }

  function renderList() {
    if (!listEl) return;
    const items = filteredTransitions();
    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("transitions.emptyFilter") : t("transitions.empty");
    }

    let lastSiteId = null;
    pageItems.forEach((tr) => {
      const siteId = tr.site_id || "";
      if (siteId !== lastSiteId) {
        lastSiteId = siteId;
        const siteTr = document.createElement("tr");
        siteTr.className = "mapsMirSiteRow";
        siteTr.innerHTML = `<td colspan="6">${escapeHtml(siteName(siteId))}</td>`;
        listEl.appendChild(siteTr);
      }

      const startLabel = positionName(tr.from_map_id, tr.start_position_id);
      const goalLabel = positionName(tr.to_map_id, tr.goal_position_id);
      const missionLabel = missionName(tr.mission_id);
      const createdBy = tr.created_by || "—";

      const trEl = document.createElement("tr");
      trEl.className = "mapsMirRow transMirRow";
      trEl.dataset.id = tr.id;

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

      trEl.innerHTML = `
        <td class="transMirCellIcon">${ICONS.transition}</td>
        <td class="transMirCellStart">
          <button type="button" class="mapsMirNameLink transMirNameLink" data-edit="${escapeHtml(tr.id)}">${escapeHtml(startLabel)}</button>
        </td>
        <td class="transMirCellGoal">${escapeHtml(goalLabel)}</td>
        <td class="transMirCellMission">${escapeHtml(missionLabel)}</td>
        <td class="mapsMirCellCreatedBy">${escapeHtml(createdBy)}</td>
        <td class="mapsMirCellActions">${actions}</td>`;

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

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

  function currentEditingTransition() {
    return store.editingId ? store.transitions.find((x) => x.id === store.editingId) : null;
  }

  function readPayload() {
    return {
      site_id: fields.site?.value || "",
      from_map_id: fields.fromMap?.value || "",
      to_map_id: fields.toMap?.value || "",
      start_position_id: fields.startPos?.value || "",
      goal_position_id: fields.goalPos?.value || "",
      mission_id: fields.mission?.value || "",
    };
  }

  function updateDependentSelects() {
    const fromMapId = fields.fromMap?.value || "";
    const toMapId = fields.toMap?.value || "";
    fillSelect(fields.startPos, positionOptionsForMap(fromMapId), fields.startPos?.value);
    fillSelect(fields.goalPos, positionOptionsForMap(toMapId), fields.goalPos?.value);
  }

  function refreshMapSelectsForSite(existing) {
    const siteOpts = store.sites.map((s) => ({ value: s.id, label: s.name || s.id }));
    const defaultSite = existing?.site_id || store.maps[0]?.site_id || siteOpts[0]?.value || "";
    fillSelect(fields.site, siteOpts, fields.site?.value || defaultSite);

    const mapsForSite = store.maps.filter((m) => (m.site_id || "") === (fields.site?.value || ""));
    const mapOpts = mapsForSite.map((m) => ({ value: m.id, label: m.name || m.id }));
    fillSelect(fields.fromMap, mapOpts, existing?.from_map_id || mapOpts[0]?.value);
    fillSelect(fields.toMap, mapOpts, existing?.to_map_id || mapOpts[0]?.value);
    updateDependentSelects();
  }

  function openDialog(id = null) {
    store.editingId = id;
    const existing = id ? store.transitions.find((x) => x.id === id) : null;
    if (titleEl) titleEl.textContent = existing ? t("transitions.editTitle") : t("transitions.createTitle");

    refreshMapSelectsForSite(existing);
    if (fields.startPos && existing?.start_position_id) fields.startPos.value = existing.start_position_id;
    if (fields.goalPos && existing?.goal_position_id) fields.goalPos.value = existing.goal_position_id;

    const missionOpts = store.missions.map((m) => ({ value: m.id, label: m.name || m.id }));
    fillSelect(fields.mission, missionOpts, existing?.mission_id || missionOpts[0]?.value);

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

  async function saveDialog() {
    if (!canWrite()) return;
    const payload = readPayload();
    if (
      !payload.site_id ||
      !payload.from_map_id ||
      !payload.to_map_id ||
      !payload.start_position_id ||
      !payload.goal_position_id ||
      !payload.mission_id
    ) {
      alert(t("transitions.error.missing"));
      return;
    }
    try {
      if (store.editingId) {
        await apiJson(`/api/transitions/${encodeURIComponent(store.editingId)}`, {
          method: "PUT",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload),
        });
      } else {
        await apiJson("/api/transitions", {
          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 tr = store.transitions.find((x) => x.id === id);
    if (!tr || !canWrite()) return;
    store.pendingDeleteId = id;
    if (deleteConfirmTextEl) {
      deleteConfirmTextEl.textContent = t("transitions.deleteConfirmText", {
        from: positionName(tr.from_map_id, tr.start_position_id),
        to: positionName(tr.to_map_id, tr.goal_position_id),
      });
    }
    deleteConfirmDialogEl?.showModal();
  }

  function openDeleteConfirmFromDialog() {
    const tr = currentEditingTransition();
    if (!tr) return;
    openDeleteConfirm(tr.id);
  }

  async function confirmDelete() {
    const id = store.pendingDeleteId || store.editingId;
    if (!id || !canWrite()) return;
    try {
      await apiJson(`/api/transitions/${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("transitionEditCancelBtn")?.addEventListener("click", () => dialogEl?.close());
    dialogEl?.addEventListener("cancel", (evt) => {
      evt.preventDefault();
      dialogEl?.close();
    });
    fields.site?.addEventListener("change", () => {
      const existing = currentEditingTransition();
      refreshMapSelectsForSite(existing);
    });
    fields.fromMap?.addEventListener("change", updateDependentSelects);
    fields.toMap?.addEventListener("change", updateDependentSelects);
    deleteBtnEl?.addEventListener("click", openDeleteConfirmFromDialog);
    el("transitionDeleteCancelBtn")?.addEventListener("click", () => {
      store.pendingDeleteId = null;
      deleteConfirmDialogEl?.close();
    });
    el("transitionDeleteYesBtn")?.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("transitionsClearFiltersBtn")?.addEventListener("click", clearFilters);
    el("transitionsPageFirst")?.addEventListener("click", () => {
      store.page = 1;
      renderList();
    });
    el("transitionsPagePrev")?.addEventListener("click", () => {
      store.page = Math.max(1, store.page - 1);
      renderList();
    });
    el("transitionsPageNext")?.addEventListener("click", () => {
      store.page += 1;
      renderList();
    });
    el("transitionsPageLast")?.addEventListener("click", () => {
      store.page = pageCount(filteredTransitions().length);
      renderList();
    });
    el("transitionsHelpBtn")?.addEventListener("click", () => {
      alert(t("transitions.helpBody"));
    });

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

  async function onPageShow() {
    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.TransitionsApp = { onPageShow, onPageHide };
})();
