import i18n from '../i18n';
import type { PageId } from './urlState';
import type { ThemePref } from './theme';

export type CommandGroup = 'nav' | 'task';

export interface CommandItem {
  id: string;
  group: CommandGroup;
  label: string;
  keywords?: string;
  hint?: string;
  run: () => void;
}

export interface CommandContext {
  navigatePage: (p: PageId) => void;
  openTask: (id: number) => void;
  setTheme: (pref: ThemePref) => void;
  navItems: Array<{ id: PageId; label: string }>;
  tasks: Array<{ id: number; title: string }>;
}

// Labels resolve against the `layout` i18n namespace at build time (this module
// has no React context); CommandPalette re-runs buildCommands on language change.
const THEMES: Array<{ pref: ThemePref; labelKey: string }> = [
  { pref: 'system', labelKey: 'commandPalette.theme.system' },
  { pref: 'light', labelKey: 'commandPalette.theme.light' },
  { pref: 'dark', labelKey: 'commandPalette.theme.dark' },
];

const EMPTY_TASK_LIMIT = 5;

export function buildCommands(ctx: CommandContext): CommandItem[] {
  const nav: CommandItem[] = ctx.navItems.map((n) => ({
    id: `nav:${n.id}`, group: 'nav', label: n.label, run: () => ctx.navigatePage(n.id),
  }));
  const theme: CommandItem[] = THEMES.map((th) => ({
    id: `theme:${th.pref}`, group: 'nav', label: i18n.t(th.labelKey, { ns: 'layout' }), run: () => ctx.setTheme(th.pref),
  }));
  const tasks: CommandItem[] = ctx.tasks.map((t) => ({
    id: `task:${t.id}`, group: 'task', label: t.title, keywords: `#${t.id}`, hint: `#${t.id}`,
    run: () => ctx.openTask(t.id),
  }));
  return [...nav, ...theme, ...tasks];
}

function score(hay: string, q: string): number {
  const i = hay.indexOf(q);
  if (i === -1) return 0;
  if (i === 0) return 3;
  if (hay.includes(' ' + q)) return 2;
  return 1;
}

export function filterCommands(items: CommandItem[], query: string): CommandItem[] {
  const q = query.trim().toLowerCase();
  if (!q) {
    const nav = items.filter((i) => i.group === 'nav');
    const tasks = items.filter((i) => i.group === 'task').slice(0, EMPTY_TASK_LIMIT);
    return [...nav, ...tasks];
  }
  return items
    .map((i) => ({ i, s: score(`${i.label} ${i.keywords ?? ''}`.toLowerCase(), q) }))
    .filter((x) => x.s > 0)
    .sort((a, b) => b.s - a.s)
    .map((x) => x.i);
}

export function groupCommands(
  items: CommandItem[],
): Array<{ group: CommandGroup; label: string; items: CommandItem[] }> {
  const order: Array<{ group: CommandGroup; label: string }> = [
    { group: 'nav', label: i18n.t('commandPalette.groups.nav', { ns: 'layout' }) },
    { group: 'task', label: i18n.t('commandPalette.groups.task', { ns: 'layout' }) },
  ];
  return order
    .map((g) => ({ ...g, items: items.filter((i) => i.group === g.group) }))
    .filter((g) => g.items.length > 0);
}

export function shouldOpenForKeyEvent(
  e: { metaKey: boolean; ctrlKey: boolean; key: string; isComposing?: boolean },
  opts: { inTerminal: boolean; inEditor: boolean },
): boolean {
  if (e.isComposing) return false;
  if (e.key.toLowerCase() !== 'k') return false;
  if (!(e.metaKey || e.ctrlKey)) return false;
  // Rich editors bind ⌘K/Ctrl-K as a chord prefix (Monaco, contenteditable):
  // never steal either combo from them.
  if (opts.inEditor) return false;
  // Terminal: only Ctrl-K is the shell binding (kill-line); ⌘K is free to open.
  if (e.ctrlKey && !e.metaKey && opts.inTerminal) return false;
  return true;
}
