// @design-system: overlays/AdminSearch

'use client';

/**
 * AdminSearch overlay — Cmd+K global search for admin pages.
 *
 * - Radix Dialog for focus trap + Esc close + aria-labelledby.
 * - 200ms debounce before calling POST /api/admin/search.
 * - Arrow keys navigate results; Enter navigates to href.
 * - Results grouped: routes first, then entities.
 *
 * i18n: strings queued in sub-b-queue.md (adminSearch namespace).
 *
 * @example
 * ```tsx
 * const [open, setOpen] = useState(false);
 * <AdminSearch open={open} onClose={() => setOpen(false)} />
 * ```
 */

import { useEffect, useRef, useState, useCallback } from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { cn } from '@/lib/cn';
import { Icon } from '@/components/ui/icons/Icon';
import { Spinner } from '@/components/ui/feedback/Spinner';
import type { AdminSearchResult } from '@/server/schemas/admin-search';
import { captureCaught } from '@/lib/observability';
import { fetchWithRefresh } from '@/lib/api/refresh-on-401';
import { useT, useLocale } from '@/lib/i18n/react';
import { dirForLocale } from '@/lib/i18n';

// ─── Types ────────────────────────────────────────────────────────────────────

export interface AdminSearchProps {
  /** Whether the overlay is open. */
  open: boolean;
  /** Called when the overlay should close. */
  onClose: () => void;
}

// ─── Helpers ──────────────────────────────────────────────────────────────────

const TYPE_ICON: Record<AdminSearchResult['type'], Parameters<typeof Icon>[0]['name']> = {
  route: 'LayoutGrid',
  vendor: 'Store',
  user: 'User',
  deal: 'Zap',
  purchase: 'ShoppingBag',
};

// ─── Component ────────────────────────────────────────────────────────────────

export function AdminSearch({ open, onClose }: AdminSearchProps) {
  const t = useT('adminSearch');
  const { locale } = useLocale();
  const dir = dirForLocale(locale);
  // `types` is a nested object in the adminSearch namespace; cast required because
  // StringBundle leaf widening types all values as `string`.
  const typeLabels = t('types' as Parameters<typeof t>[0]) as unknown as Record<
    AdminSearchResult['type'],
    string
  >;
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<AdminSearchResult[]>([]);
  const [loading, setLoading] = useState(false);
  const [activeIndex, setActiveIndex] = useState(-1);

  const inputRef = useRef<HTMLInputElement>(null);
  const listRef = useRef<HTMLUListElement>(null);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Reset transient UI state when the dialog opens, then focus the search input.
  useEffect(() => {
    if (open) {
      queueMicrotask(() => {
        setQuery('');
        setResults([]);
        setActiveIndex(-1);
      });
      setTimeout(() => inputRef.current?.focus(), 50);
    }
  }, [open]);

  // Debounced search
  const doSearch = useCallback(async (q: string) => {
    if (!q.trim()) {
      setResults([]);
      setLoading(false);
      return;
    }
    setLoading(true);
    try {
      const res = await fetchWithRefresh('/api/admin/search', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ q: q.trim(), limit: 20 }),
      });
      if (res.ok) {
        const data = (await res.json()) as { ok: true; results: AdminSearchResult[] };
        setResults(data.results ?? []);
      } else {
        setResults([]);
      }
    } catch (err) {
      captureCaught(err, {
        scope: 'components.ui.overlays.AdminSearch.AdminSearch',
        severity: 'warning',
      });
      setResults([]);
    } finally {
      setLoading(false);
    }
  }, []);

  function handleInput(e: React.ChangeEvent<HTMLInputElement>) {
    const val = e.target.value;
    setQuery(val);
    setActiveIndex(-1);
    if (debounceRef.current) clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => doSearch(val), 200);
  }

  const navigate = useCallback(
    (href: string) => {
      window.location.href = href;
      onClose();
    },
    [onClose],
  );

  function handleKeyDown(e: React.KeyboardEvent) {
    if (!results.length) return;
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setActiveIndex((i) => Math.min(i + 1, results.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setActiveIndex((i) => Math.max(i - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      const result = results[activeIndex] ?? results[0];
      if (result) {
        navigate(result.href);
      }
    }
  }

  // Scroll active item into view
  useEffect(() => {
    if (activeIndex < 0) return;
    const item = listRef.current?.children[activeIndex] as HTMLElement | undefined;
    item?.scrollIntoView({ block: 'nearest' });
  }, [activeIndex]);

  return (
    <Dialog.Root
      open={open}
      onOpenChange={(o) => {
        if (!o) onClose();
      }}
    >
      <Dialog.Portal>
        {/* Overlay */}
        <Dialog.Overlay
          className={cn(
            'fixed inset-0 z-[200] bg-black/40',
            'data-[state=open]:animate-in data-[state=open]:fade-in-0',
            'data-[state=closed]:animate-out data-[state=closed]:fade-out-0',
            'motion-reduce:animate-none motion-reduce:transition-none',
          )}
        />

        {/* Panel */}
        <Dialog.Content
          aria-label={t('dialog_aria')}
          onKeyDown={handleKeyDown}
          className={cn(
            'fixed inset-x-0 top-[10vh] z-[201] mx-auto w-full max-w-lg',
            'bg-surface-base rounded-xl shadow-lg',
            'border-border-default border',
            'flex flex-col overflow-hidden',
            'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
            'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
            'motion-reduce:animate-none motion-reduce:transition-none',
            'focus-visible:outline-none',
          )}
        >
          <Dialog.Title className="sr-only">{t('dialog_aria')}</Dialog.Title>

          {/* Input row */}
          <div className="border-border-default flex items-center gap-3 border-b px-4 py-3">
            <Icon name="Search" size="sm" color="muted" aria-hidden />
            <input
              ref={inputRef}
              type="search"
              value={query}
              onChange={handleInput}
              placeholder={t('placeholder')}
              dir={dir}
              autoComplete="off"
              aria-label={t('placeholder')}
              aria-controls="admin-search-results"
              aria-expanded={results.length > 0}
              role="combobox"
              aria-haspopup="listbox"
              aria-activedescendant={
                activeIndex >= 0 ? `admin-search-result-${activeIndex}` : undefined
              }
              className={cn(
                'text-text-primary min-w-0 flex-1 bg-transparent text-sm',
                'placeholder:text-text-secondary',
                'outline-none',
                'focus-visible:rounded-[var(--radius-sm)] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-brand-primary-500)]',
              )}
            />
            {loading && <Spinner variant="inline" size="sm" />}
            <Dialog.Close asChild>
              <button
                type="button"
                aria-label={t('close_aria')}
                className={cn(
                  'text-text-secondary rounded p-1',
                  'hover:text-text-primary hover:bg-neutral-100',
                  'focus-visible:outline-brand-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
                )}
              >
                <Icon name="X" size="sm" aria-hidden />
              </button>
            </Dialog.Close>
          </div>

          {/* Results */}
          {results.length > 0 ? (
            <ul
              id="admin-search-results"
              ref={listRef}
              role="listbox"
              aria-label={t('results_aria')}
              className="max-h-72 overflow-y-auto py-2"
            >
              {results.map((result, i) => (
                <li
                  key={result.id}
                  id={`admin-search-result-${i}`}
                  role="option"
                  aria-selected={i === activeIndex}
                  className={cn(
                    'flex items-center gap-3 px-4 py-2.5 text-sm',
                    'cursor-pointer transition-colors',
                    i === activeIndex
                      ? 'bg-brand-primary-50 text-brand-primary-700'
                      : 'text-text-primary hover:bg-neutral-50',
                  )}
                  onMouseEnter={() => setActiveIndex(i)}
                  onClick={() => {
                    navigate(result.href);
                  }}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' || e.key === ' ') {
                      e.preventDefault();
                      navigate(result.href);
                    }
                  }}
                >
                  <Icon
                    name={TYPE_ICON[result.type]}
                    size="sm"
                    color={i === activeIndex ? 'primary' : 'muted'}
                    aria-hidden
                  />
                  <div className="min-w-0 flex-1" dir={dir}>
                    <div className="truncate font-medium">{result.title}</div>
                    {result.subtitle && (
                      <div className="text-text-secondary truncate text-xs">{result.subtitle}</div>
                    )}
                  </div>
                  <span className="shrink-0 rounded bg-neutral-100 px-1.5 py-0.5 text-xs text-neutral-500">
                    {typeLabels[result.type]}
                  </span>
                </li>
              ))}
            </ul>
          ) : query && !loading ? (
            <div className="text-text-secondary px-4 py-8 text-center text-sm" aria-live="polite">
              {t('noResults')}
            </div>
          ) : !query ? (
            <div className="text-text-secondary px-4 py-6 text-center text-sm">
              {t('start_typing')}
            </div>
          ) : null}

          {/* Footer hint */}
          <div
            className="border-border-default text-text-secondary border-t px-4 py-2 text-xs"
            dir={dir}
          >
            <span className="me-3">
              <kbd className="rounded bg-neutral-100 px-1 py-0.5 font-mono">↑↓</kbd>{' '}
              {t('kbd_navigate')}
            </span>
            <span className="me-3">
              <kbd className="rounded bg-neutral-100 px-1 py-0.5 font-mono">Enter</kbd>{' '}
              {t('kbd_open')}
            </span>
            <span>
              <kbd className="rounded bg-neutral-100 px-1 py-0.5 font-mono">Esc</kbd>{' '}
              {t('kbd_close')}
            </span>
          </div>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  );
}
