"use client";

/**
 * CommandPalette — Cmd+K / Ctrl+K global quick-launcher for the admin.
 *
 * Behavior:
 *   - Toggled with Cmd+K (mac) or Ctrl+K (win/linux) on `document`.
 *   - Closes on Escape, on backdrop click, or after a navigation.
 *   - Fuzzy case-insensitive match on `label` / `category` / `keywords`.
 *   - Top 8 results. Renders null when closed (no DOM noise).
 *   - Keyboard: ↑/↓ to move, Enter to select, type to filter.
 *
 * Self-contained: does not read from the sidebar, does not depend on any
 * server data. The static list lives in `./command-palette-data.ts`.
 *
 * The component is intentionally NOT mounted in the admin layout by this
 * file. The main thread / sidebar agent wires it in once it's reviewed.
 */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import {
  BarChart3,
  Clock,
  Command,
  CreditCard,
  Droplets,
  FileText,
  LayoutDashboard,
  LucideIcon,
  Map as MapIcon,
  MapPin,
  Megaphone,
  Package,
  Plug,
  PlusCircle,
  Puzzle,
  Receipt,
  RefreshCw,
  Route,
  ScrollText,
  Search,
  Send,
  Settings,
  ShoppingCart,
  Sparkles,
  Store,
  Tag,
  Truck,
  Upload,
  UserPlus,
  Users,
  Wallet,
} from "lucide-react";
import { PALETTE_ENTRIES, type PaletteEntry } from "./command-palette-data";

const MAX_RESULTS = 8;

// ---------------------------------------------------------------------------
// Icon registry
// ---------------------------------------------------------------------------

const ICON_MAP: Record<string, LucideIcon> = {
  // Pages
  LayoutDashboard,
  Command,
  ShoppingCart,
  MapPin,
  Package,
  Truck,
  Send,
  Megaphone,
  FileText,
  Users,
  ScrollText,
  Store,
  Upload,
  Sparkles,
  Clock,
  Droplets,
  Route,
  BarChart3,
  Receipt,
  Settings,
  Tag,
  CreditCard,
  Plug,
  Wallet,
  Puzzle,
  // Quick actions
  PlusCircle,
  UserPlus,
  RefreshCw,
  // Misc / fallbacks
  Search,
  Map: MapIcon,
};

// ---------------------------------------------------------------------------
// Filtering / scoring
// ---------------------------------------------------------------------------

function scoreEntry(entry: PaletteEntry, query: string): number {
  if (!query) return 1;
  const q = query.toLowerCase();
  const label = entry.label.toLowerCase();
  const category = entry.category.toLowerCase();

  let score = 0;
  if (label === q) score += 100;
  else if (label.startsWith(q)) score += 50;
  else if (label.includes(q)) score += 25;

  if (category.toLowerCase().includes(q)) score += 10;
  if (entry.keywords?.some((k) => k.toLowerCase().includes(q))) score += 5;

  // Quick actions get a small boost when no specific match (so they surface
  // on the empty query list, alongside the first batch of pages).
  if (!query && entry.type === "action") score = 0.5;
  if (!query && entry.type === "page") score = 1 - entry.label.length * 0.001;

  return score;
}

function filterEntries(query: string): PaletteEntry[] {
  const scored = PALETTE_ENTRIES
    .map((e) => ({ entry: e, score: scoreEntry(e, query) }))
    .filter((r) => r.score > 0)
    .sort((a, b) => b.score - a.score)
    .slice(0, MAX_RESULTS);
  return scored.map((r) => r.entry);
}

// ---------------------------------------------------------------------------
// Highlight helper
// ---------------------------------------------------------------------------

function HighlightedText({ text, query }: { text: string; query: string }) {
  if (!query) return <>{text}</>;
  const q = query.toLowerCase();
  const idx = text.toLowerCase().indexOf(q);
  if (idx === -1) return <>{text}</>;
  return (
    <>
      {text.slice(0, idx)}
      <mark
        style={{
          background: "transparent",
          color: "var(--admin-primary)",
          fontWeight: 600,
          padding: 0,
        }}
      >
        {text.slice(idx, idx + query.length)}
      </mark>
      {text.slice(idx + query.length)}
    </>
  );
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

export default function CommandPalette() {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState("");
  const [selected, setSelected] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);
  const router = useRouter();

  // Open/close on Cmd+K / Ctrl+K. Toggle so the same shortcut closes it.
  useEffect(() => {
    function onKeyDown(e: KeyboardEvent) {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setOpen((o) => !o);
      }
    }
    document.addEventListener("keydown", onKeyDown);
    return () => document.removeEventListener("keydown", onKeyDown);
  }, []);

  // Reset on open, focus the input, and lock body scroll.
  useEffect(() => {
    if (!open) {
      document.body.style.overflow = "";
      return;
    }
    setQuery("");
    setSelected(0);
    document.body.style.overflow = "hidden";
    // Defer focus to next frame so the input is mounted and the modal
    // animation has started (keeps the focus ring from flickering in).
    const raf = requestAnimationFrame(() => inputRef.current?.focus());
    return () => {
      cancelAnimationFrame(raf);
      document.body.style.overflow = "";
    };
  }, [open]);

  // Global Escape while open (covers the case where focus is not on input).
  useEffect(() => {
    if (!open) return;
    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") {
        e.preventDefault();
        setOpen(false);
      }
    }
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [open]);

  // Filtering
  const results = useMemo(() => filterEntries(query), [query]);

  // Reset selection on every query change so the top result is always active.
  useEffect(() => {
    setSelected(0);
  }, [query]);

  // Clamp selection to results length
  useEffect(() => {
    if (selected >= results.length && results.length > 0) {
      setSelected(results.length - 1);
    } else if (results.length === 0) {
      setSelected(0);
    }
  }, [results, selected]);

  const navigate = useCallback(
    (href: string) => {
      setOpen(false);
      router.push(href);
    },
    [router]
  );

  // In-input keyboard nav. Attached to the input itself so it works whether
  // the user is typing or the input just has focus.
  const handleInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.key === "ArrowDown") {
      e.preventDefault();
      if (results.length === 0) return;
      setSelected((s) => (s + 1) % results.length);
    } else if (e.key === "ArrowUp") {
      e.preventDefault();
      if (results.length === 0) return;
      setSelected((s) => (s - 1 + results.length) % results.length);
    } else if (e.key === "Enter") {
      e.preventDefault();
      const entry = results[selected];
      if (entry) navigate(entry.href);
    }
  };

  if (!open) return null;

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-label="Command palette"
      onClick={() => setOpen(false)}
      style={{
        position: "fixed",
        inset: 0,
        zIndex: 60,
        backgroundColor: "rgba(26, 24, 20, 0.4)",
        backdropFilter: "blur(4px)",
        WebkitBackdropFilter: "blur(4px)",
        display: "flex",
        alignItems: "flex-start",
        justifyContent: "center",
        paddingTop: "15vh",
        animation: "cp-fade-in 180ms ease-out",
      }}
    >
      {/* Local keyframes — scoped via the dialog's runtime so they don't
          leak into the global stylesheet. The panel now fades in without
          the scale(0.98) start (removed — the slight scale combined with
          the backdrop fade read as a "pop" when the palette opened). */}
      <style>{`
        @keyframes cp-fade-in {
          from { opacity: 0; }
          to   { opacity: 1; }
        }
        @keyframes cp-scale-in {
          from { opacity: 0; }
          to   { opacity: 1; }
        }
        @media (prefers-reduced-motion: reduce) {
          @keyframes cp-fade-in { from { opacity: 0; } to { opacity: 1; } }
          @keyframes cp-scale-in { from { opacity: 0; } to { opacity: 1; } }
        }
      `}</style>

      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: "100%",
          maxWidth: "36rem",
          margin: "0 1rem",
          backgroundColor: "var(--admin-card-bg)",
          border: "1px solid var(--admin-border)",
          borderRadius: "var(--admin-radius-lg)",
          boxShadow: "var(--admin-shadow-lg)",
          overflow: "hidden",
          animation: "cp-scale-in 180ms ease-out",
          display: "flex",
          flexDirection: "column",
        }}
      >
        {/* Search input row */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            gap: "0.75rem",
            padding: "0.875rem 1rem",
            borderBottom: "1px solid var(--admin-border)",
          }}
        >
          <Search
            size={18}
            style={{ color: "var(--admin-text-muted)", flexShrink: 0 }}
            aria-hidden="true"
          />
          <input
            ref={inputRef}
            type="text"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onKeyDown={handleInputKeyDown}
            placeholder="Search pages, actions, anything…"
            aria-label="Search command palette"
            spellCheck={false}
            autoComplete="off"
            style={{
              flex: 1,
              minWidth: 0,
              border: "none",
              outline: "none",
              background: "transparent",
              fontSize: "1rem",
              lineHeight: 1.4,
              color: "var(--admin-text-primary)",
              fontFamily: "inherit",
            }}
          />
        </div>

        {/* Results */}
        <div
          role="listbox"
          aria-label="Results"
          style={{
            maxHeight: "60vh",
            overflowY: "auto",
            padding: "0.375rem",
          }}
        >
          {/*
            TODO (next pass): recent items
            - Persist a small ring buffer of last-visited entries in localStorage
              under "rc-cmdk-recent" (cap at 5).
            - Read on mount, render as a "Recent" section above "Pages" when
              present and the query is empty.
            - Update on `navigate()` after a successful route push.
            Skipped for v1 per the design spec.
          */}
          {results.length === 0 ? (
            <div
              style={{
                padding: "2rem 1rem",
                textAlign: "center",
                color: "var(--admin-text-muted)",
                fontSize: "0.875rem",
              }}
            >
              No results for &ldquo;{query}&rdquo;
            </div>
          ) : (
            results.map((entry, i) => {
              const Icon = ICON_MAP[entry.iconName] ?? Search;
              const isSelected = i === selected;
              return (
                <button
                  key={entry.id}
                  type="button"
                  role="option"
                  aria-selected={isSelected}
                  onClick={() => navigate(entry.href)}
                  onMouseEnter={() => setSelected(i)}
                  style={{
                    display: "flex",
                    alignItems: "center",
                    gap: "0.75rem",
                    width: "100%",
                    padding: "0.625rem 0.75rem",
                    borderRadius: "var(--admin-radius-md)",
                    border: "none",
                    background: isSelected
                      ? "var(--admin-primary-soft)"
                      : "transparent",
                    color: isSelected
                      ? "var(--admin-primary)"
                      : "var(--admin-text-primary)",
                    cursor: "pointer",
                    textAlign: "left",
                    fontSize: "0.875rem",
                    lineHeight: 1.3,
                    fontFamily: "inherit",
                    transition:
                      "background-color 80ms ease-out, color 80ms ease-out",
                  }}
                >
                  <Icon
                    size={16}
                    style={{
                      flexShrink: 0,
                      opacity: isSelected ? 1 : 0.7,
                    }}
                    aria-hidden="true"
                  />
                  <span
                    style={{
                      flex: 1,
                      minWidth: 0,
                      overflow: "hidden",
                      textOverflow: "ellipsis",
                      whiteSpace: "nowrap",
                      fontWeight: 500,
                    }}
                  >
                    <HighlightedText text={entry.label} query={query} />
                  </span>
                  <span
                    style={{
                      fontSize: "0.6875rem",
                      textTransform: "uppercase",
                      letterSpacing: "0.05em",
                      color: "var(--admin-text-muted)",
                      fontWeight: 500,
                      flexShrink: 0,
                    }}
                  >
                    {entry.category}
                  </span>
                </button>
              );
            })
          )}
        </div>

        {/* Footer hint */}
        <div
          style={{
            padding: "0.5rem 1rem 0.625rem",
            borderTop: "1px solid var(--admin-border)",
            fontSize: "0.6875rem",
            color: "var(--admin-text-muted)",
            textAlign: "center",
            letterSpacing: "0.02em",
            userSelect: "none",
          }}
        >
          ↑↓ to navigate &nbsp;·&nbsp; ↵ to select &nbsp;·&nbsp; esc to close
        </div>
      </div>
    </div>
  );
}
