/**
 * MultiSelect — compact dropdown multi-select filter component.
 *
 * Observable-compatible: exposes `.value` (string[]) and dispatches bubbling
 * `input` events on change, so it works with `view()`, `Inputs.form`, and
 * `Generators.input`.
 *
 * Usage:
 *   const el = MultiSelect(["a", "b", "c"], {label: "Domain"});
 *   const selected = view(el);  // string[] — empty means "all / no filter"
 */

const STYLE_ID = "ms-component-styles";

function ensureStyles() {
  if (typeof document === "undefined" || document.getElementById(STYLE_ID)) return;
  const s = document.createElement("style");
  s.id = STYLE_ID;
  s.textContent = `
.ms-wrap {
  position: relative;
  display: inline-block;
  font-family: var(--sans-serif, system-ui, sans-serif);
  font-size: 0.85rem;
}
.ms-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.28rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--theme-foreground-faint, #ccc);
  background: var(--theme-background, #fff);
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  white-space: nowrap;
  transition: border-color 0.15s, background 0.15s;
  user-select: none;
  color: var(--theme-foreground, #111);
}
.ms-trigger:hover {
  border-color: var(--theme-foreground-muted, #888);
}
.ms-trigger.ms-has-selection {
  border-color: steelblue;
  background: color-mix(in srgb, steelblue 8%, var(--theme-background, #fff));
}
.ms-trigger-label {
  color: var(--theme-foreground-muted, #666);
}
.ms-trigger-value {
  font-weight: 500;
  color: var(--theme-foreground, #111);
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ms-trigger-value.ms-placeholder {
  font-weight: 400;
  color: var(--theme-foreground-muted, #888);
}
.ms-chevron {
  font-size: 0.65rem;
  color: var(--theme-foreground-muted, #888);
  transition: transform 0.15s;
  line-height: 1;
}
.ms-wrap.ms-open .ms-chevron {
  transform: rotate(180deg);
}
.ms-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 5px);
  left: 0;
  z-index: 1000;
  min-width: max(100%, 160px);
  background: var(--theme-background, #fff);
  border: 1px solid var(--theme-foreground-faint, #ddd);
  border-radius: 8px;
  box-shadow: 0 4px 18px rgba(0,0,0,0.12);
  padding: 0.35rem 0;
}
.ms-wrap.ms-open .ms-dropdown {
  display: block;
}
.ms-clear {
  display: block;
  width: 100%;
  padding: 0.2rem 0.75rem 0.35rem;
  font: inherit;
  font-size: 0.75rem;
  color: steelblue;
  background: none;
  border: none;
  text-align: left;
  cursor: pointer;
  border-bottom: 1px solid var(--theme-foreground-faint, #eee);
  margin-bottom: 0.2rem;
}
.ms-clear:hover { text-decoration: underline; }
.ms-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.28rem 0.75rem;
  cursor: pointer;
  border-radius: 0;
}
.ms-option:hover {
  background: var(--theme-background-alt, #f5f5f5);
}
.ms-option input[type=checkbox] {
  margin: 0;
  cursor: pointer;
  accent-color: steelblue;
  flex-shrink: 0;
}
`;
  document.head.append(s);
}

/**
 * @param {string[] | {value: string, label: string}[]} options
 * @param {{ label?: string, value?: string[], placeholder?: string }} opts
 * @returns {HTMLElement}
 */
export function MultiSelect(options, { label = "", value = [], placeholder = "All" } = {}) {
  ensureStyles();

  // Normalise options to {value, label} pairs
  const opts = options.map(o => typeof o === "string" ? { value: o, label: o } : o);
  let selected = new Set(value);

  // ── Build DOM ──────────────────────────────────────────────────────────────

  const wrap = document.createElement("div");
  wrap.className = "ms-wrap";

  const trigger = document.createElement("button");
  trigger.type = "button";
  trigger.className = "ms-trigger";

  const labelSpan = document.createElement("span");
  labelSpan.className = "ms-trigger-label";
  if (label) labelSpan.textContent = label + ":";

  const valueSpan = document.createElement("span");

  const chevron = document.createElement("span");
  chevron.className = "ms-chevron";
  chevron.textContent = "▾";

  if (label) trigger.append(labelSpan, "\u00a0"); // non-breaking space between label and value
  trigger.append(valueSpan, chevron);

  // Dropdown
  const dropdown = document.createElement("div");
  dropdown.className = "ms-dropdown";

  const clearBtn = document.createElement("button");
  clearBtn.type = "button";
  clearBtn.className = "ms-clear";
  clearBtn.textContent = "Clear selection";
  dropdown.append(clearBtn);

  const checkboxes = opts.map(opt => {
    const row = document.createElement("label");
    row.className = "ms-option";
    const cb = document.createElement("input");
    cb.type = "checkbox";
    cb.value = opt.value;
    cb.checked = selected.has(opt.value);
    row.append(cb, opt.label);
    dropdown.append(row);
    return cb;
  });

  wrap.append(trigger, dropdown);

  // ── State helpers ──────────────────────────────────────────────────────────

  function syncUI() {
    const n = selected.size;
    if (n === 0) {
      valueSpan.textContent = placeholder;
      valueSpan.className = "ms-trigger-value ms-placeholder";
      trigger.classList.remove("ms-has-selection");
      clearBtn.style.display = "none";
    } else {
      const names = [...selected].map(v => opts.find(o => o.value === v)?.label ?? v);
      valueSpan.textContent = n <= 2 ? names.join(", ") : `${n} of ${opts.length}`;
      valueSpan.className = "ms-trigger-value";
      trigger.classList.add("ms-has-selection");
      clearBtn.style.display = "block";
    }
  }

  function emit() {
    syncUI();
    wrap.dispatchEvent(new Event("input", { bubbles: true }));
  }

  // ── Open / close ───────────────────────────────────────────────────────────

  function open() {
    // Close any other open dropdowns on the page
    document.querySelectorAll(".ms-wrap.ms-open").forEach(w => {
      if (w !== wrap) w.classList.remove("ms-open");
    });
    wrap.classList.add("ms-open");
  }

  function close() {
    wrap.classList.remove("ms-open");
  }

  // ── Events ─────────────────────────────────────────────────────────────────

  trigger.addEventListener("click", e => {
    e.stopPropagation();
    wrap.classList.contains("ms-open") ? close() : open();
  });

  checkboxes.forEach((cb, i) => {
    cb.addEventListener("change", () => {
      if (cb.checked) selected.add(opts[i].value);
      else selected.delete(opts[i].value);
      emit();
    });
  });

  clearBtn.addEventListener("click", e => {
    e.stopPropagation();
    selected.clear();
    checkboxes.forEach(cb => (cb.checked = false));
    emit();
  });

  // Prevent dropdown clicks from bubbling to the document closer
  dropdown.addEventListener("click", e => e.stopPropagation());

  document.addEventListener("click", close);
  document.addEventListener("keydown", e => { if (e.key === "Escape") close(); });

  // ── Observable compatibility ───────────────────────────────────────────────

  // Empty array = "no filter" (show all). Semantics: any checked item = restrict to those.
  Object.defineProperty(wrap, "value", {
    get: () => [...selected],
    enumerable: true,
  });

  syncUI();
  return wrap;
}
