"use client";

import type { ReactNode } from "react";
import { useCallback, useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { Command, Search } from "lucide-react";
import { SearchModal } from "./search-modal";

const TOGGLE_SEARCH_EVENT = "shell-docs:toggle-search";

function isEditableTarget(target: EventTarget | null): boolean {
  if (!target || !(target instanceof HTMLElement)) return false;
  const tag = target.tagName;
  if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
  if (target.isContentEditable) return true;
  return false;
}

export function ShellSearchProvider({ children }: { children: ReactNode }) {
  const [open, setOpen] = useState(false);
  const closeSearch = useCallback(() => setOpen(false), []);
  const toggleSearch = useCallback(() => setOpen((prev) => !prev), []);

  useEffect(() => {
    function onKeyDown(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        // Don't hijack Cmd/Ctrl+K when the user is typing in an unrelated
        // input / textarea / contenteditable — only steal the shortcut when
        // focus is outside an editable element or already inside our own
        // search modal.
        const target = e.target as HTMLElement | null;
        const insideSearchModal =
          target?.closest?.("[data-search-modal]") != null;
        if (isEditableTarget(target) && !insideSearchModal) return;

        e.preventDefault();
        e.stopPropagation();
        toggleSearch();
      }
      if (e.key === "Escape") closeSearch();
    }

    document.addEventListener("keydown", onKeyDown, { capture: true });
    window.addEventListener(TOGGLE_SEARCH_EVENT, toggleSearch);

    return () => {
      document.removeEventListener("keydown", onKeyDown, { capture: true });
      window.removeEventListener(TOGGLE_SEARCH_EVENT, toggleSearch);
    };
  }, [closeSearch, toggleSearch]);

  return (
    <>
      {children}
      {open && <SearchModalWrapper onClose={closeSearch} />}
    </>
  );
}

function toggleShellSearch() {
  window.dispatchEvent(new Event(TOGGLE_SEARCH_EVENT));
}

export function SearchTrigger({
  iconOnly = false,
}: { iconOnly?: boolean } = {}) {
  // Start as null so SSR output matches the initial client render; resolve
  // after mount to avoid hydration mismatch flashing ⌘K → Ctrl+K on non-Mac.
  const [isMac, setIsMac] = useState<boolean | null>(null);

  useEffect(() => {
    const mac =
      typeof navigator !== "undefined" && /mac/i.test(navigator.userAgent);
    setIsMac(mac);
  }, []);

  if (iconOnly) {
    return (
      <button
        onClick={toggleShellSearch}
        className="shell-docs-radius-control flex h-10 w-10 cursor-pointer items-center justify-center border border-[var(--border)] bg-[var(--bg-surface)] text-[var(--text-muted)] shadow-[var(--shadow-control)] transition-colors hover:border-[var(--accent)] hover:bg-[var(--bg-elevated)] hover:text-[var(--text)]"
        aria-label="Search"
        title="Search"
      >
        <Search className="h-4 w-4" aria-hidden="true" />
      </button>
    );
  }

  // Mirrors the canonical `search-button.tsx` chrome: same height as the
  // navbar's right-cluster controls, icon + label on lg+, ⌘K hint on xl+.
  return (
    <>
      <button
        onClick={toggleShellSearch}
        aria-label="Search"
        className="shell-docs-radius-control flex h-10 w-10 cursor-pointer items-center gap-2 border border-[var(--border)] bg-[var(--bg-surface)] px-2.5 text-[var(--text-muted)] shadow-[var(--shadow-control)] transition-colors hover:border-[var(--accent)] hover:bg-[var(--bg-elevated)] hover:text-[var(--text)] lg:w-[220px] xl:w-[260px]"
      >
        <Search className="h-4 w-4 shrink-0" aria-hidden="true" />

        <span className="hidden flex-1 text-left text-sm font-medium lg:block">
          Search
        </span>

        <span
          className="shell-docs-radius-control hidden min-w-[3.25rem] items-center justify-center gap-1 border border-[var(--border)] bg-[var(--bg-surface)] px-1.5 py-0.5 font-mono text-[11px] text-[var(--text-faint)] xl:inline-flex"
          // Reserve horizontal room so the button doesn't reflow when the
          // shortcut hint appears after hydration.
          suppressHydrationWarning
        >
          {isMac === null ? (
            "\u00a0"
          ) : isMac ? (
            <>
              <Command className="h-3 w-3" aria-hidden="true" />K
            </>
          ) : (
            "Ctrl K"
          )}
        </span>
      </button>
    </>
  );
}

function SearchModalWrapper({ onClose }: { onClose: () => void }) {
  // Portal to document.body so the modal's `position: fixed` resolves
  // against the viewport. The trigger renders inside the navbar's right
  // cluster, which uses `backdrop-blur-lg` — backdrop-filter creates a
  // containing block for fixed-position descendants, which would
  // otherwise clamp the overlay to the cluster's bounding rect instead
  // of covering the page.
  if (typeof document === "undefined") return null;
  return createPortal(
    <div data-search-modal>
      <SearchModal onClose={onClose} />
    </div>,
    document.body,
  );
}
