import { createEffect, createMemo, createSignal, mergeProps } from "solid-js";

function ListManager(props) {
  props = mergeProps(
    {
      items: [],
      availableItems: [],
      itemToString: () => "",
      style: "",
      onItemSelect: () => {},
      onItemDeselect: () => {},
      itemsTitle: "Selected items",
      availableItemsTitle: "Available items",
    },
    props
  );

  const itemToString = (item) => props.itemToString(item);
  const [selectedAvailableItemIndex, setSelectedAvailableItemIndex] =
    createSignal(-1);
  const [selectedItemIndex, setSelectedItemIndex] = createSignal(-1);
  const [itemsSearchString, setItemsSearchString] = createSignal("");
  const [availableItemsSearchString, setAvailableItemsSearchString] =
    createSignal("");

  const itemsFuse = createMemo(
    () =>
      new Fuse(props.items, {
        keys: [{ name: "label", getFn: (item) => props.itemToString(item) }],
      })
  );
  const availableItemsFuse = createMemo(
    () =>
      new Fuse(props.availableItems, {
        keys: [{ name: "label", getFn: (item) => props.itemToString(item) }],
      })
  );

  const selectableAvailableItems = createMemo(() =>
    (availableItemsSearchString()
      ? availableItemsFuse()
          .search(availableItemsSearchString())
          .map((item) => item.item)
      : props.availableItems
    ).filter((item) => !props.items.includes(item))
  );
  const selectableItems = createMemo(() =>
    itemsSearchString()
      ? itemsFuse().search(itemsSearchString()).map((item) => item.item)
      : props.items
  );
  const canSelect = createMemo(
    () =>
      selectedAvailableItemIndex() >= 0 &&
      selectedAvailableItemIndex() < selectableAvailableItems().length
  );
  const canDeselect = createMemo(
    () =>
      selectedItemIndex() >= 0 && selectedItemIndex() < selectableItems().length
  );

  function handleSelectItem() {
    let itemIndex = selectedAvailableItemIndex();
    let item = selectableAvailableItems()[itemIndex];
    if (itemIndex === selectableAvailableItems().length - 1) {
      setSelectedAvailableItemIndex(itemIndex - 1);
    }
    props.onItemSelect(item);
  }

  function handleDeselectItem() {
    let itemIndex = selectedItemIndex();
    let item = selectableItems()[itemIndex];
    if (itemIndex === selectableItems().length - 1) {
      setSelectedItemIndex(itemIndex - 1);
    }
    props.onItemDeselect(item);
  }

  function ListItem(props) {
    props = mergeProps(
      { children: "", selected: false, onClick: () => {} },
      props
    );
    return (
      <div
        class={"px-2" + (props.selected ? " bg-secondary" : "")}
        role="button"
        onClick={props.onClick}
      >
        {props.children}
      </div>
    );
  }

  function ItemList(props) {
    props = mergeProps(
      {
        items: [],
        onItemSelected: () => {},
        selectedItemIndex: -1,
        onSearchStringChange: () => {},
      },
      props
    );

    return (
      <>
        <div class="input-group input-group-sm">
          <span
            class="input-group-text border-bottom-0 rounded-bottom-0"
            id="basic-addon1"
          >
            <i class="bi bi-search"></i>
          </span>
          <input
            type="text"
            class="form-control form-control-sm border-bottom-0 rounded-bottom-0"
            onInput={(event) => props.onSearchStringChange(event.target.value)}
          />
        </div>
        <div class="rounded rounded-top-0 border bg-body flex-fill overflow-y-scroll">
          {props.items.map((item, index) => (
            <ListItem
              onClick={() => props.onItemSelected(index)}
              selected={index === props.selectedItemIndex}
            >
              {itemToString(item)}
            </ListItem>
          ))}
        </div>
      </>
    );
  }

  return (
    <div class={"d-flex"} style={props.style}>
      <div class="flex-shrink-1 w-50 d-flex flex-column">
        <div class="px-2">{props.itemsTitle}</div>
        <ItemList
          items={selectableItems()}
          onItemSelected={(index) => setSelectedItemIndex(index)}
          selectedItemIndex={selectedItemIndex()}
          onSearchStringChange={(value) => setItemsSearchString(value)}
        />
      </div>
      <div class="p-2 d-flex flex-column justify-content-center align-items-center">
        <button
          class="btn btn-sm btn-outline-secondary mb-2"
          onClick={handleSelectItem}
          disabled={!canSelect()}
        >
          <i class="bi bi-caret-left-fill"></i>
        </button>
        <button
          class="btn btn-sm btn-outline-secondary"
          onClick={handleDeselectItem}
          disabled={!canDeselect()}
        >
          <i class="bi bi-caret-right-fill"></i>
        </button>
      </div>
      <div class="flex-shrink-1 w-50 d-flex flex-column">
        <div class="px-2">{props.availableItemsTitle}</div>
        <ItemList
          items={selectableAvailableItems()}
          onItemSelected={(index) => setSelectedAvailableItemIndex(index)}
          selectedItemIndex={selectedAvailableItemIndex()}
          onSearchStringChange={(value) => setAvailableItemsSearchString(value)}
        />
      </div>
    </div>
  );
}

export default ListManager;
