import { createFloatingLayer, type FloatingLayerController, type FloatingLayerOptions } from "./floating.js";

export interface SelectControllerOptions<T extends string> extends Omit<FloatingLayerOptions, "closeOnSelect"> {
  value: T;
  onChange: (value: T) => void;
  valueTarget?: HTMLElement;
}

export interface SelectController<T extends string> extends FloatingLayerController {
  getValue: () => T;
  setValue: (value: T, emit?: boolean) => void;
}

export function createSelectController<T extends string>({
  value,
  onChange,
  valueTarget,
  surface,
  trigger,
  ...floatingOptions
}: SelectControllerOptions<T>): SelectController<T> {
  let currentValue = value;
  const floating = createFloatingLayer({
    trigger,
    surface,
    closeOnSelect: true,
    ...floatingOptions,
  });

  const options = () => Array.from(surface.querySelectorAll<HTMLElement>("[data-nodedc-option][data-nodedc-value]"));

  const sync = () => {
    options().forEach((option) => {
      const selected = option.dataset.nodedcValue === currentValue;
      option.dataset.selected = selected ? "true" : "false";
      option.setAttribute("aria-selected", String(selected));
      if (selected && valueTarget) valueTarget.textContent = option.dataset.nodedcLabel ?? option.textContent ?? currentValue;
    });
  };

  const setValue = (nextValue: T, emit = true) => {
    currentValue = nextValue;
    sync();
    if (emit) onChange(nextValue);
  };

  const handleOptionClick = (event: MouseEvent) => {
    if (!(event.target instanceof HTMLElement)) return;
    const option = event.target.closest<HTMLElement>("[data-nodedc-option][data-nodedc-value]");
    if (!option || option.hasAttribute("disabled")) return;
    setValue(option.dataset.nodedcValue as T);
  };

  surface.addEventListener("click", handleOptionClick);
  sync();

  return {
    ...floating,
    getValue: () => currentValue,
    setValue,
    destroy: () => {
      surface.removeEventListener("click", handleOptionClick);
      floating.destroy();
    },
  };
}

