(function () {
  const picker = document.querySelector("[data-reaction-picker]");
  const panel = picker ? picker.querySelector(".reaction-picker-panel") : null;
  const search = picker ? picker.querySelector("[data-reaction-search]") : null;
  const closeButton = picker ? picker.querySelector("[data-reaction-close]") : null;
  const existingSection = picker ? picker.querySelector("[data-reaction-existing]") : null;
  const existingList = picker ? picker.querySelector("[data-reaction-existing-list]") : null;
  const readonlyNote = picker ? picker.querySelector("[data-reaction-readonly]") : null;
  const chooserElements = picker ? Array.from(picker.querySelectorAll(".reaction-picker-tabs, .reaction-search, .reaction-grid-wrap")) : [];
  const tabs = picker ? Array.from(picker.querySelectorAll("[data-reaction-tab]")) : [];
  const panels = picker ? Array.from(picker.querySelectorAll("[data-reaction-panel]")) : [];

  let activeButton = null;
  let activeCard = null;

  document.querySelectorAll("[data-reaction-button]").forEach((button) => {
    button.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      openPicker(button);
    });
  });

  document.addEventListener("click", (event) => {
    const pill = event.target.closest("[data-reaction-pill]");
    if (pill) {
      event.preventDefault();
      event.stopPropagation();
      const card = pill.closest("[data-reaction-card]") || activeCard;
      if (!card) {
        return;
      }
      if (card.dataset.reacted === "true") {
        openPickerForCard(card, pill);
        return;
      }
      submitReactionForCard(card, pill.dataset.reactionEmojiId);
      return;
    }

    const more = event.target.closest("[data-reaction-more]");
    if (!more) {
      return;
    }

    event.preventDefault();
    event.stopPropagation();
    const card = more.closest("[data-reaction-card]");
    if (card) {
      openPickerForCard(card, more);
    }
  });

  if (!picker || !panel) {
    return;
  }

  // Aurora's glass card uses backdrop-filter, and the main content animates
  // with transform. Both can create a containing block for fixed descendants,
  // so keep the floating picker at body level where viewport coordinates mean
  // what they say.
  document.body.appendChild(picker);

  picker.addEventListener("click", (event) => {
    if (event.target === picker) {
      closePicker();
    }
  });

  panel.addEventListener("click", async (event) => {
    const emoji = event.target.closest("[data-emoji-id]");
    if (!emoji || !activeCard || activeCard.dataset.reacted === "true") {
      return;
    }
    await submitReactionForCard(activeCard, emoji.dataset.emojiId);
  });

  tabs.forEach((tab) => {
    tab.addEventListener("click", () => {
      setActiveTab(tab.dataset.reactionTab);
    });
  });

  if (search) {
    search.addEventListener("input", () => filterEmoji(search.value));
  }

  if (closeButton) {
    closeButton.addEventListener("click", closePicker);
  }

  document.addEventListener("click", (event) => {
    if (picker.hidden) {
      return;
    }
    if (panel.contains(event.target) || event.target.closest("[data-reaction-button]")) {
      return;
    }
    if (event.target.closest("[data-reaction-more]") || event.target.closest("[data-reaction-pill]")) {
      return;
    }
    closePicker();
  });

  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape") {
      closePicker();
    }
  });

  window.addEventListener("resize", () => {
    if (activeButton && !picker.hidden) {
      positionPicker(activeButton);
    }
  });

  function openPicker(button) {
    openPickerForCard(button.closest("[data-reaction-card]"), button);
  }

  function openPickerForCard(card, trigger) {
    if (!card) {
      return;
    }
    activeButton = trigger || card.querySelector("[data-reaction-button]");
    activeCard = card;
    populateExistingReactions(card);
    setPickerReadonly(card.dataset.reacted === "true");
    picker.hidden = false;
    picker.classList.add("is-open");
    if (search) {
      search.value = "";
      filterEmoji("");
    }
    positionPicker(activeButton || card);
  }

  function closePicker() {
    picker.hidden = true;
    picker.classList.remove("is-open", "is-mobile");
    document.documentElement.classList.remove("reaction-picker-open");
    picker.style.left = "";
    picker.style.top = "";
    setPickerReadonly(false);
    activeButton = null;
    activeCard = null;
  }

  function positionPicker(button) {
    if (isMobilePicker()) {
      picker.classList.add("is-mobile");
      document.documentElement.classList.add("reaction-picker-open");
      picker.style.left = "0px";
      picker.style.top = "0px";
      return;
    }

    picker.classList.remove("is-mobile");
    document.documentElement.classList.remove("reaction-picker-open");
    picker.style.left = "0px";
    picker.style.top = "0px";
    const buttonRect = button.getBoundingClientRect();
    const pickerRect = panel.getBoundingClientRect();
    const margin = 10;
    const preferredLeft = buttonRect.left + (buttonRect.width / 2) - (pickerRect.width / 2);
    const preferredTop = buttonRect.bottom + 8;
    const left = Math.min(Math.max(margin, preferredLeft), window.innerWidth - pickerRect.width - margin);
    const top = Math.min(Math.max(margin, preferredTop), window.innerHeight - pickerRect.height - margin);
    picker.style.left = `${left}px`;
    picker.style.top = `${top}px`;
  }

  function isMobilePicker() {
    return window.matchMedia("(max-width: 820px), (pointer: coarse)").matches;
  }

  function setActiveTab(tabID) {
    tabs.forEach((tab) => {
      const active = tab.dataset.reactionTab === tabID;
      tab.classList.toggle("is-active", active);
      tab.setAttribute("aria-selected", active ? "true" : "false");
    });
    panels.forEach((item) => {
      item.classList.toggle("is-active", item.dataset.reactionPanel === tabID);
    });
  }

  function filterEmoji(value) {
    const query = value.trim().toLowerCase();
    picker.querySelectorAll("[data-emoji-id]").forEach((button) => {
      const haystack = `${button.dataset.emojiId} ${button.dataset.emojiLabel}`.toLowerCase();
      button.hidden = query !== "" && !haystack.includes(query);
    });
  }

  async function submitReactionForCard(card, emojiID) {
    if (!card || !emojiID || card.dataset.reacted === "true") {
      return;
    }
    const body = new URLSearchParams();
    body.set("emoji_id", emojiID);

    const reactButton = card.querySelector("[data-reaction-button]");
    if (reactButton) {
      reactButton.disabled = true;
    }
    const response = await fetch(card.dataset.reactUrl, {
      method: "POST",
      headers: {
        "Accept": "application/json",
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body,
    });

    if (!response.ok) {
      if (reactButton) {
        reactButton.disabled = false;
      }
      closePicker();
      return;
    }

    const payload = await response.json();
    renderReactions(card, payload.reactions || []);
    card.dataset.reacted = "true";
    if (reactButton) {
      reactButton.remove();
    }
    closePicker();
  }

  function renderReactions(card, reactions) {
    const list = card.querySelector("[data-reaction-list]");
    if (!list) {
      return;
    }
    list.replaceChildren();
    reactions.forEach((reaction) => {
      const pill = buildReactionPill(reaction);
      if (!reaction.visible) {
        pill.classList.add("is-hidden-summary");
      }
      list.append(pill);
    });
    const hiddenCount = reactions.length > 2 ? reactions.length - 2 : 0;
    if (hiddenCount > 0) {
      const more = document.createElement("button");
      more.className = "reaction-more";
      more.type = "button";
      more.dataset.reactionMore = "";
      more.textContent = `+${hiddenCount}`;
      more.setAttribute("aria-label", `Show ${hiddenCount} more reactions`);
      list.append(more);
    }
  }

  function buildReactionPill(reaction) {
    const pill = document.createElement("button");
    pill.className = "reaction-pill";
    pill.type = "button";
    pill.title = reaction.label || reaction.emojiId;
    pill.dataset.reactionPill = "";
    pill.dataset.reactionEmojiId = reaction.emojiId;
    pill.dataset.reactionLabel = reaction.label || reaction.emojiId;
    pill.dataset.reactionUrl = reaction.url;
    pill.dataset.reactionCount = reaction.count;
    pill.setAttribute("aria-label", `React with ${reaction.label || reaction.emojiId}`);

    const image = document.createElement("img");
    image.src = reaction.url;
    image.alt = reaction.label || reaction.emojiId;
    image.loading = "lazy";

    const count = document.createElement("span");
    count.textContent = reaction.count;

    pill.append(image, count);
    return pill;
  }

  function populateExistingReactions(card) {
    if (!existingSection || !existingList) {
      return;
    }
    existingList.replaceChildren();
    card.querySelectorAll("[data-reaction-pill]").forEach((pill) => {
      const clone = pill.cloneNode(true);
      clone.classList.remove("is-hidden-summary");
      existingList.append(clone);
    });
    existingSection.hidden = existingList.children.length === 0;
  }

  function setPickerReadonly(readonly) {
    picker.classList.toggle("is-readonly", readonly);
    chooserElements.forEach((element) => {
      element.hidden = readonly;
    });
    if (readonlyNote) {
      readonlyNote.hidden = !readonly;
    }
  }
})();
