import { Signal, useSignal } from "@preact/signals";
import { useRef } from "preact/hooks";
import { useEventListener } from "@lib/hooks/useEventListener.ts";
import { menus } from "@islands/KMenu/commands.ts";
import { MenuEntry } from "@islands/KMenu/types.ts";

const KMenuEntry = (
  { entry, activeIndex, index }: {
    entry: MenuEntry;
    activeIndex: Signal<number>;
    index: number;
  },
) => {
  return (
    <div
      onClick={() => activeIndex.value = index}
      class={`px-4 py-2 ${
        activeIndex.value === index
          ? "bg-gray-100 text-gray-900"
          : "text-gray-400"
      }`}
    >
      {entry.title}
    </div>
  );
};

export const KMenu = (
  { type = "main", context }: { context: unknown; type: keyof typeof menus },
) => {
  const activeMenuType = useSignal(type);
  const activeMenu = menus[activeMenuType.value || "main"];
  const activeState = useSignal<"normal" | "loading" | "error" | "input">(
    "normal",
  );
  const activeIndex = useSignal(-1);

  const input = useRef<HTMLInputElement>(null);
  const commandInput = useSignal("");

  const visible = useSignal(false);
  if (visible.value === false) {
    setTimeout(() => {
      activeMenuType.value = "main";
      activeState.value = "normal";
      if (input.current) {
        input.current.value = "";
      }
      commandInput.value = "";
    }, 100);
  }

  const entries = activeMenu?.entries?.filter((entry) => {
    const entryVisible = typeof entry["visible"] === "boolean"
      ? entry.visible
      : typeof entry["visible"] === "function"
      ? entry.visible?.(context)
      : true;

    const search = commandInput?.value?.toLowerCase();
    const searchMatches = entry.title.toLowerCase().includes(search) ||
      entry.meta?.toLowerCase()?.includes(search);

    return entryVisible && searchMatches;
  });

  if (entries.length === 1) {
    activeIndex.value = 0;
  }

  function activateEntry(menuEntry?: MenuEntry) {
    if (!menuEntry) return;

    menuEntry.cb({
      activeMenu: activeMenuType,
      menus,
      activeState,
      commandInput,
      visible,
    }, context);
  }

  useEventListener("keydown", (ev: KeyboardEvent) => {
    if (ev.key === "/" && ev.ctrlKey) {
      visible.value = !visible.value;
    }

    if (ev.key === "ArrowDown") {
      const index = activeIndex.value;
      if (index + 1 >= entries.length) {
        activeIndex.value = 0;
      } else {
        activeIndex.value += 1;
      }
    }

    if (ev.key === "Enter") {
      activateEntry(entries[activeIndex.value]);
    }

    if (ev.key === "ArrowUp") {
      const index = activeIndex.value;
      if (index - 1 < 0) {
        activeIndex.value = entries.length - 1;
      } else {
        activeIndex.value -= 1;
      }
    }

    if (ev.key === "Escape") {
      visible.value = false;
      if (input.current) {
        input.current.value = "";
      }
    }

    if (visible.value) {
      input.current?.focus();
    } else {
      input.current?.blur();
    }
  });

  return (
    <div
      class={`opacity-${visible.value ? 100 : 0} pointer-events-${
        visible.value ? "auto" : "none"
      } transition grid place-items-center w-full h-full fixed top-0 left-0 z-50`}
      style={{ background: "#1f1f1f88" }}
    >
      <div
        class={`relative w-1/2 max-h-64 max-w-[400px] rounded-2xl shadow-2xl nnoisy-gradient overflow-hidden after:opacity-10`}
        style={{ background: "#1f1f1f" }}
      >
        <div
          class="grid h-12 text-gray-400 border-b border-gray-500 "
          style={{ gridTemplateColumns: "4em 1fr" }}
        >
          {(activeState.value === "normal" || activeState.value === "input") &&
            (
              <>
                <div class="grid place-items-center border-r border-gray-500">
                  <span class="text-white">
                    {activeMenu.title}
                  </span>
                </div>
                <input
                  ref={input}
                  onInput={() => {
                    commandInput.value = input.current?.value || "";
                  }}
                  placeholder="Command"
                  class="bg-transparent color pl-4 border-0"
                />
              </>
            )}
          {activeState.value === "loading" && (
            <div class="p-4">
              Loading...
            </div>
          )}
        </div>
        {activeState.value === "normal" &&
          (
            <div class="" style={{ maxHeight: "12rem", overflowY: "auto" }}>
              {entries?.length === 0 && (
                <div class="text-gray-400 px-4 py-2">
                  No Entries
                </div>
              )}
              {entries.map(
                (k, index) => {
                  return (
                    <KMenuEntry
                      entry={k}
                      activeIndex={activeIndex}
                      index={index}
                    />
                  );
                },
              )}
            </div>
          )}
      </div>
    </div>
  );
};
