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";
import * as icons from "@components/icons.tsx";
import { IS_BROWSER } from "$fresh/runtime.ts";
const KMenuEntry = (
  { entry, activeIndex, index }: {
    entry: MenuEntry;
    activeIndex: Signal<number>;
    index: number;
  },
) => {
  return (
    <div
      onClick={() => activeIndex.value = index}
      class={`px-4 py-2 flex items-center gap-1 ${
        activeIndex.value === index
          ? "bg-gray-100 text-gray-900"
          : "text-gray-400"
      }`}
    >
      {entry?.icon && icons[entry.icon]({ class: "w-4 h-4 mr-1" })}
      {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 loadingText = useSignal("");
  const activeIndex = useSignal(-1);

  const containerRef = useRef<HTMLDivElement>(null);
  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,
      loadingText,
      menus,
      activeState,
      commandInput,
      visible,
    }, context);
  }

  const container = containerRef.current;
  if (container) {
    const selectedItem = container.children[activeIndex.value];
    if (selectedItem) {
      const itemPosition = selectedItem.getBoundingClientRect();
      const containerPosition = container.getBoundingClientRect();

      // Check if the selected item is above the visible area
      if (itemPosition.top < containerPosition.top) {
        container.scrollTop -= containerPosition.top - itemPosition.top;
      } // Check if the selected item is below the visible area
      else if (itemPosition.bottom > containerPosition.bottom) {
        container.scrollTop += itemPosition.bottom - containerPosition.bottom;
      }
    }
  }

  useEventListener("keydown", (ev: KeyboardEvent) => {
    if (ev.key === "k") {
      if (ev?.target?.nodeName == "INPUT") {
        return;
      }

      if (!visible.value) {
        ev.preventDefault();
        ev.stopPropagation();
      }

      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 (input.current) {
        input.current.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();
    }
  }, IS_BROWSER ? document?.body : undefined);

  return (
    <div
      class={`${visible.value ? "opacity-100" : "opacity-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: "#141217ee" }}
    >
      <div
        class={`relative w-1/2 max-h-64 max-w-[400px] rounded-2xl shadow-2xl nnoisy-gradient overflow-hidden after:opacity-10 bg-gray-700`}
        style={{ background: "#2B2930", color: "#818181" }}
      >
        <div
          class={`grid h-12 text-gray-400 ${
            activeState.value !== "loading" && "border-b"
          } border-gray-500 `}
          style={{
            gridTemplateColumns: activeState.value !== "loading"
              ? "auto 1fr"
              : "1fr",
          }}
        >
          {(activeState.value === "normal" || activeState.value === "input") &&
            (
              <>
                <div class="grid place-items-center border-r border-gray-500">
                  <span class="text-white mx-4">
                    {activeMenu.title}
                  </span>
                </div>
                <input
                  ref={input}
                  onInput={() => {
                    commandInput.value = input.current?.value || "";
                  }}
                  style={{ outline: "none !important" }}
                  placeholder="Command"
                  class="bg-transparent color pl-4 outline outline outline-2  outline-offset-2"
                />
              </>
            )}
          {activeState.value === "loading" && (
            <div class="py-3 px-4 flex items-center gap-2">
              <icons.IconLoader2 class="animate-spin w-4 h-4" />
              {loadingText.value || "Loading..."}
            </div>
          )}
        </div>
        {activeState.value === "normal" &&
          (
            <div
              class=""
              style={{ maxHeight: "12rem", overflowY: "auto" }}
              ref={containerRef}
            >
              {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>
  );
};
