const closeEventName = "nodedc-select-open";

const makeSelectId = () => `ndc-select-${Math.random().toString(16).slice(2)}`;

export function createGlassSelect({
  host,
  value,
  options = [],
  ariaLabel = "Выбрать",
  disabled = false,
  onChange = null,
}) {
  if (!host) return null;

  const selectId = makeSelectId();
  let currentValue = value;
  let currentOptions = [...options];
  let open = false;
  let menu = null;
  let menuRect = { top: 0, left: 0, width: 0 };

  host.innerHTML = "";

  const root = document.createElement("div");
  root.className = "nodedc-select";

  const control = document.createElement("div");
  control.className = "nodedc-select__control";

  const valueEl = document.createElement("div");
  valueEl.className = "nodedc-select__value";

  const toggle = document.createElement("button");
  toggle.type = "button";
  toggle.className = "nodedc-select__toggle";
  toggle.setAttribute("aria-label", ariaLabel);
  toggle.setAttribute("aria-haspopup", "listbox");
  toggle.disabled = !!disabled;

  const chevron = document.createElement("span");
  chevron.className = "nodedc-select__chevron";
  chevron.setAttribute("aria-hidden", "true");
  toggle.appendChild(chevron);

  control.appendChild(valueEl);
  control.appendChild(toggle);
  root.appendChild(control);
  host.appendChild(root);

  const selectedOption = () => (
    currentOptions.find((option) => option.value === currentValue) ||
    currentOptions[0] ||
    null
  );

  const updateValue = () => {
    const selected = selectedOption();
    valueEl.textContent = selected?.label || "";
    toggle.setAttribute("aria-expanded", open ? "true" : "false");
  };

  const updateMenuRect = () => {
    const rect = root.getBoundingClientRect();
    const toggleWidth = 42;
    const gap = 8;
    const menuMaxHeight = 232;
    const bottomTop = rect.bottom + 6;
    const top = bottomTop + menuMaxHeight > window.innerHeight - 12
      ? Math.max(12, rect.top - menuMaxHeight - 6)
      : bottomTop;

    menuRect = {
      top,
      left: rect.left,
      width: Math.max(180, rect.width - toggleWidth - gap),
    };

    if (menu) {
      menu.style.top = `${menuRect.top}px`;
      menu.style.left = `${menuRect.left}px`;
      menu.style.width = `${menuRect.width}px`;
    }
  };

  const renderMenuOptions = () => {
    if (!menu) return;
    menu.innerHTML = "";
    currentOptions.forEach((option) => {
      const item = document.createElement("button");
      item.type = "button";
      item.className = "nodedc-dropdown-option nodedc-select__option";
      item.dataset.value = option.value;
      if (option.value === currentValue) {
        item.dataset.selected = "true";
      }
      item.setAttribute("role", "option");
      item.setAttribute("aria-selected", option.value === currentValue ? "true" : "false");
      item.textContent = option.label;
      item.addEventListener("click", () => {
        setValue(option.value, { emit: true });
        closeMenu();
      });
      menu.appendChild(item);
    });
  };

  const openMenu = () => {
    if (toggle.disabled || open) return;
    open = true;
    updateValue();
    updateMenuRect();
    window.dispatchEvent(new CustomEvent(closeEventName, { detail: { id: selectId } }));

    menu = document.createElement("div");
    menu.className = "nodedc-dropdown-surface nodedc-select__menu";
    menu.setAttribute("role", "listbox");
    menu.style.position = "fixed";
    menu.style.top = `${menuRect.top}px`;
    menu.style.left = `${menuRect.left}px`;
    menu.style.width = `${menuRect.width}px`;
    renderMenuOptions();
    document.body.appendChild(menu);

    document.addEventListener("mousedown", handleDocumentPointerDown);
    document.addEventListener("keydown", handleKeyDown);
    window.addEventListener(closeEventName, handleOtherSelectOpen);
    window.addEventListener("resize", updateMenuRect);
    window.addEventListener("scroll", updateMenuRect, true);
  };

  function closeMenu() {
    if (!open) return;
    open = false;
    updateValue();
    if (menu) {
      menu.remove();
      menu = null;
    }
    document.removeEventListener("mousedown", handleDocumentPointerDown);
    document.removeEventListener("keydown", handleKeyDown);
    window.removeEventListener(closeEventName, handleOtherSelectOpen);
    window.removeEventListener("resize", updateMenuRect);
    window.removeEventListener("scroll", updateMenuRect, true);
  }

  function handleDocumentPointerDown(event) {
    const target = event.target;
    if (
      target instanceof Node &&
      !root.contains(target) &&
      (!menu || !menu.contains(target))
    ) {
      closeMenu();
    }
  }

  function handleKeyDown(event) {
    if (event.key === "Escape") closeMenu();
  }

  function handleOtherSelectOpen(event) {
    if (event?.detail?.id !== selectId) closeMenu();
  }

  function setValue(nextValue, { emit = false } = {}) {
    const nextOption = currentOptions.find((option) => option.value === nextValue);
    if (!nextOption) return;
    const changed = currentValue !== nextValue;
    currentValue = nextValue;
    updateValue();
    renderMenuOptions();
    if (emit && changed && typeof onChange === "function") {
      onChange(nextValue);
    }
  }

  const setOptions = (nextOptions = [], nextValue = currentValue) => {
    currentOptions = [...nextOptions];
    currentValue = currentOptions.some((option) => option.value === nextValue)
      ? nextValue
      : currentOptions[0]?.value;
    updateValue();
    renderMenuOptions();
  };

  const setDisabled = (nextDisabled) => {
    toggle.disabled = !!nextDisabled;
    if (toggle.disabled) closeMenu();
  };

  const destroy = () => {
    closeMenu();
    root.remove();
  };

  toggle.addEventListener("click", () => {
    if (open) {
      closeMenu();
    } else {
      openMenu();
    }
  });
  valueEl.addEventListener("click", openMenu);

  updateValue();

  return {
    root,
    setValue,
    setOptions,
    setDisabled,
    close: closeMenu,
    destroy,
    getValue: () => currentValue,
  };
}
