/* commands.ts — Slash commands (/command) for the WebUI */
/// <reference path="./global.d.ts" />

interface CmdDef {
  name: string;
  desc: string;
  arg: string;
  aliases: string[];
  fn: (args: string) => void;
}

const PASSTHROUGH = ['retry','undo','title','branch','stop','background','btw',
  'queue','status','profile','resume','snapshot','rollback','provider',
  'yolo','reasoning','fast','voice','reload','reload-mcp','cron','browser',
  'plugins','insights','platforms','debug','update','image','inbox'];

let COMMANDS: CmdDef[] = [];

const AGENT_INFO: Record<string, { emoji: string; name: string; file: string; domain: string }> = {
  'sunflower':     { emoji: '\uD83C\uDF3B', name: 'Sunflower',    file: 'sunflower/soul.md',     domain: 'Finance, Wealth & Subscriptions' },
  'lotus':         { emoji: '\uD83E\uDDD7', name: 'Lotus',        file: 'lotus/soul.md',         domain: 'Health, Fitness & Recovery' },
  'forget-me-not': { emoji: '\uD83C\uDF3C', name: 'Forget-me-not', file: 'forget-me-not/soul.md', domain: 'Calendar, Time & Social' },
  'iris':          { emoji: '\u2695\uFE0F', name: 'Iris',         file: 'iris/soul.md',          domain: 'Career, Learning & Focus' },
  'ivy':           { emoji: '\uD83C\uDF3F', name: 'Ivy',          file: 'ivy/soul.md',           domain: 'Smart Home & Environment' },
  'dandelion':     { emoji: '\uD83D\uDEE1', name: 'Dandelion',    file: 'dandelion/soul.md',     domain: 'Communication Triage & Gatekeeping' },
  'root':          { emoji: '\uD83C\uDF33', name: 'Root',         file: 'root/soul.md',          domain: 'DevOps, Logs & System Health' },
  'back':          { emoji: '\uD83C\uDF39', name: 'Rose',         file: 'rose/soul.md',          domain: 'Orchestrator (return from agent)' },
};

function _fnFor(name: string): (args: string) => void {
  if (name === 'help' || name === 'commands') return cmdHelp;
  if (name === 'clear') return cmdClear;
  if (name === 'compact' || name === 'compress') return cmdCompact;
  if (name === 'model') return cmdModel;
  if (name === 'workspace') return cmdWorkspace;
  if (name === 'new') return cmdNew;
  if (name === 'usage') return cmdUsage;
  if (name === 'theme') return cmdTheme;
  if (name === 'skills') return cmdSkills;
  if (name === 'personality') return cmdPersonality;
  if (PASSTHROUGH.includes(name)) return cmdPassthrough;
  return cmdPassthrough;
}

async function loadCommands(): Promise<void> {
  try {
    const data = await api('/api/commands') as { error?: string; categories?: Record<string, Array<{ name: string; desc: string; arg?: string; aliases?: string[] }>> };
    if (data.error) throw new Error(data.error);
    const cats = data.categories || {};
    const merged: CmdDef[] = [];
    for (const [, cmds] of Object.entries(cats)) {
      for (const c of cmds) {
        merged.push({ name: c.name, desc: c.desc, arg: c.arg || '(none)', aliases: c.aliases || [], fn: _fnFor(c.name) });
      }
    }
    const agentNames = ['sunflower','lotus','forget-me-not','iris','ivy','dandelion','root','back','inbox'];
    const filtered = merged.filter(c => !agentNames.includes(c.name));
    filtered.push(
      { name: 'sunflower',     desc: '\uD83C\uDF3B Finance, Wealth & Subscriptions', fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'lotus',         desc: '\uD83E\uDDD7 Health, Fitness & Recovery',      fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'forget-me-not', desc: '\uD83C\uDF3C Calendar, Time & Social',         fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'iris',          desc: '\u2695\uFE0F Career, Learning & Focus',        fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'ivy',           desc: '\uD83C\uDF3F Smart Home & Environment',         fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'dandelion',     desc: '\uD83D\uDEE1 Communication Triage & Gatekeeping', fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'root',          desc: '\uD83C\uDF33 DevOps, Logs & System Health',    fn: cmdAgent, arg: 'message', aliases: [] },
      { name: 'back',          desc: '\uD83C\uDF39 Return to Rose (orchestrator)',    fn: cmdAgent, arg: 'message', aliases: [] },
    );
    COMMANDS = filtered;
  } catch (e: unknown) {
    console.warn('[commands] Failed to load from API, using fallback:', (e instanceof Error) ? e.message : String(e));
    COMMANDS = [];
  }
}

interface ParsedCommand { name: string; args: string; }

function parseCommand(text: string): ParsedCommand | null {
  if (!text.startsWith('/')) return null;
  const parts = text.slice(1).split(/\s+/);
  const name = parts[0].toLowerCase();
  const args = parts.slice(1).join(' ').trim();
  return { name, args };
}

function executeCommand(text: string): boolean {
  const parsed = parseCommand(text);
  if (!parsed) return false;
  const cmd = COMMANDS.find(c => c.name === parsed.name);
  if (!cmd) return false;
  cmd.fn(parsed.args);
  return true;
}

function getMatchingCommands(prefix: string): CmdDef[] {
  const q = prefix.toLowerCase();
  return COMMANDS.filter(c => {
    if (c.name.startsWith(q)) return true;
    if (c.aliases && c.aliases.some(a => a.startsWith(q))) return true;
    return false;
  });
}

function cmdPassthrough(_args: string): void {
  const msgEl = $('msg') as HTMLTextAreaElement | null;
  if (!msgEl) return;
  const parsed = parseCommand(msgEl.value);
  if (!parsed) return;
  send();
}

function cmdHelp(): void {
  const categories: Record<string, CmdDef[]> = { 'Session': [], 'Configuration': [], 'Tools & Skills': [], 'Info': [], 'Agents': [] };
  COMMANDS.forEach(c => {
    let cat = 'Info';
    if (['new','clear','compact','compress','retry','undo','title','branch','stop','background','btw','queue','status','profile','resume','snapshot','rollback'].includes(c.name)) cat = 'Session';
    else if (['model','provider','personality','workspace','theme','yolo','reasoning','fast','voice','reload','reload-mcp'].includes(c.name)) cat = 'Configuration';
    else if (['skills','cron','browser','plugins'].includes(c.name)) cat = 'Tools & Skills';
    else if (['sunflower','lotus','forget-me-not','iris','ivy','dandelion','root','back','inbox'].includes(c.name)) cat = 'Agents';
    if (!categories[cat]) categories[cat] = [];
    categories[cat].push(c);
  });
  const lines: string[] = [];
  for (const [cat, cmds] of Object.entries(categories)) {
    if (!cmds.length) continue;
    lines.push(`\n**${cat}**`);
    cmds.forEach(c => {
      const usage = c.arg && c.arg !== '(none)' ? ` <${c.arg}>` : '';
      lines.push(`  /${c.name}${usage} \u2014 ${c.desc}`);
    });
  }
  const msg = { role: 'assistant' as const, content: 'Available commands:\n' + lines.join('\n') };
  S.messages.push(msg);
  renderMessages();
  showToast(t('type_slash'));
}

function cmdClear(): void {
  if (!S.session) return;
  S.messages = []; S.toolCalls = [];
  clearLiveToolCards();
  renderMessages();
  const emptyState = $('emptyState');
  if (emptyState) emptyState.style.display = '';
  showToast(t('conversation_cleared'));
}

async function cmdModel(args: string): Promise<void> {
  if (!args) { showToast('Usage: /model <model_name>'); return; }
  const sel = $('modelSelect') as unknown as HTMLSelectElement | null;
  if (!sel) return;
  const q = args.toLowerCase();
  let match: string | null = null;
  for (const opt of sel.options) {
    if (opt.value.toLowerCase().includes(q) || (opt.textContent || '').toLowerCase().includes(q)) { match = opt.value; break; }
  }
  if (!match) { showToast('No model matching "' + args + '"'); return; }
  sel.value = match;
  if (sel.onchange) await (sel as HTMLSelectElement).onchange!(null as unknown as Event);
  showToast(t('switched_to') + match);
}

async function cmdWorkspace(args: string): Promise<void> {
  if (!args) { showToast('Usage: /workspace <name>'); return; }
  try {
    const data = await api('/api/workspaces') as { workspaces?: Array<{ name?: string; path: string }> };
    const q = args.toLowerCase();
    const ws = (data.workspaces || []).find(w =>
      (w.name || '').toLowerCase().includes(q) || w.path.toLowerCase().includes(q)
    );
    if (!ws) { showToast('No workspace matching "' + args + '"'); return; }
    if (typeof switchToWorkspace === 'function') await switchToWorkspace(ws.path, ws.name || ws.path);
    else showToast(t('switched_workspace') + (ws.name || ws.path));
  } catch (e: unknown) { showToast(t('workspace_switch_failed') + ((e instanceof Error) ? e.message : String(e))); }
}

async function cmdNew(): Promise<void> {
  await newSession();
  await renderSessionList();
  const msgEl = $('msg') as HTMLTextAreaElement | null;
  if (msgEl) msgEl.focus();
  showToast(t('new_session'));
}

function cmdCompact(): void {
  const msgEl = $('msg') as HTMLTextAreaElement | null;
  if (msgEl) msgEl.value = 'Please compress and summarize the conversation context to free up space.';
  send();
  showToast(t('compressing'));
}

async function cmdUsage(): Promise<void> {
  const next = !window._showTokenUsage;
  window._showTokenUsage = next;
  try { await api('/api/settings', { method: 'POST', body: JSON.stringify({ show_token_usage: next }) }); } catch { /* noop */ }
  const cb = $('settingsShowTokenUsage') as HTMLInputElement | null;
  if (cb) cb.checked = next;
  renderMessages();
  showToast(next ? t('token_usage_on') : t('token_usage_off'));
}

async function cmdTheme(args: string): Promise<void> {
  const themes = ['system','dark','light','slate','solarized','monokai','nord','oled'];
  if (!args || !themes.includes(args.toLowerCase())) { showToast('Themes: ' + themes.join(' | ')); return; }
  const themeName = args.toLowerCase();
  localStorage.setItem('hermes-theme', themeName);
  _applyTheme(themeName);
  try { await api('/api/settings', { method: 'POST', body: JSON.stringify({ theme: themeName }) }); } catch { /* noop */ }
  const sel = $('settingsTheme') as unknown as HTMLSelectElement | null;
  if (sel) sel.value = themeName;
  showToast(t('theme_set') + themeName);
}

async function cmdSkills(args: string): Promise<void> {
  try {
    const data = await api('/api/skills') as { skills?: Array<{ name?: string; description?: string; category?: string }> };
    let skills = data.skills || [];
    if (args) {
      const q = args.toLowerCase();
      skills = skills.filter(s =>
        (s.name || '').toLowerCase().includes(q) ||
        (s.description || '').toLowerCase().includes(q) ||
        (s.category || '').toLowerCase().includes(q)
      );
    }
    if (!skills.length) {
      const msg = { role: 'assistant' as const, content: args ? `No skills matching "${args}".` : 'No skills found.' };
      S.messages.push(msg); renderMessages(); return;
    }
    const byCategory: Record<string, typeof skills> = {};
    skills.forEach(s => {
      const cat = s.category || 'General';
      if (!byCategory[cat]) byCategory[cat] = [];
      byCategory[cat].push(s);
    });
    const lines: string[] = [];
    for (const [cat, items] of Object.entries(byCategory).sort()) {
      lines.push(`**${cat}**`);
      items.forEach(s => {
        const desc = s.description ? ` \u2014 ${s.description.slice(0, 80)}${s.description.length > 80 ? '...' : ''}` : '';
        lines.push(`  \`${s.name}\`${desc}`);
      });
      lines.push('');
    }
    const header = args ? `Skills matching "${args}" (${skills.length}):\n\n` : `Available skills (${skills.length}):\n\n`;
    S.messages.push({ role: 'assistant' as const, content: header + lines.join('\n') });
    renderMessages();
  } catch (e: unknown) { showToast('Failed to load skills: ' + (e instanceof Error ? e.message : String(e))); }
}

async function cmdPersonality(args: string): Promise<void> {
  if (!S.session) { showToast(t('no_active_session')); return; }
  if (!args) {
    try {
      const data = await api('/api/personalities') as { personalities?: Array<{ name: string; description?: string }> };
      if (!data.personalities || !data.personalities.length) { showToast(t('no_personalities')); return; }
      const list = data.personalities.map(p => `  **${p.name}**${p.description ? ' \u2014 ' + p.description : ''}`).join('\n');
      S.messages.push({ role: 'assistant' as const, content: t('available_personalities') + '\n\n' + list + '\n\nSwitch with: /personality <name>' });
      renderMessages();
    } catch { showToast(t('personalities_load_failed')); }
    return;
  }
  const name = args.trim();
  if (['none','default','clear'].includes(name.toLowerCase())) {
    try {
      await api('/api/personality/set', { method: 'POST', body: JSON.stringify({ session_id: S.session.session_id, name: '' }) });
      showToast(t('personality_cleared'));
    } catch (e: unknown) { showToast(t('failed_colon') + (e instanceof Error ? e.message : String(e))); }
    return;
  }
  try {
    await api('/api/personality/set', { method: 'POST', body: JSON.stringify({ session_id: S.session.session_id, name }) });
    showToast(t('personality_set') + name);
  } catch (e: unknown) { showToast(t('failed_colon') + (e instanceof Error ? e.message : String(e))); }
}

function cmdAgent(_args: string): void {
  const msgEl = $('msg') as HTMLTextAreaElement | null;
  if (!msgEl) return;
  const parsed = parseCommand(msgEl.value);
  if (!parsed) return;
  const agentKey = parsed.name;
  const info = AGENT_INFO[agentKey];
  if (!info) { showToast('Unknown agent: ' + agentKey); return; }
  const userMsg = _args || '';
  const contextMsg = `[Agent Switch: ${info.emoji} ${info.name}]\nLoad ~/.hermes/agents/${info.file} and handle this request as ${info.name} (${info.domain}).${userMsg ? '\n\nUser message: ' + userMsg : ''}`;
  msgEl.value = contextMsg;
  send();
}

let _cmdSelectedIdx = -1;

function showCmdDropdown(matches: CmdDef[]): void {
  const dd = $('cmdDropdown');
  if (!dd) return;
  dd.innerHTML = '';
  _cmdSelectedIdx = -1;
  for (let i = 0; i < matches.length; i++) {
    const c = matches[i];
    const el = document.createElement('div');
    el.className = 'cmd-item';
    (el as HTMLElement).dataset.idx = String(i);
    const usage = c.arg && c.arg !== '(none)' ? ` <span class="cmd-item-arg">${esc(c.arg)}</span>` : '';
    el.innerHTML = `<div class="cmd-item-name">/${esc(c.name)}${usage}</div><div class="cmd-item-desc">${esc(c.desc)}</div>`;
    el.addEventListener('mousedown', (e) => {
      e.preventDefault();
      const msgEl2 = $('msg') as HTMLTextAreaElement | null;
      if (msgEl2) {
        msgEl2.value = '/' + c.name + (c.arg && c.arg !== '(none)' ? ' ' : '');
        msgEl2.focus();
      }
      hideCmdDropdown();
    });
    dd.appendChild(el);
  }
  dd.classList.add('open');
}

function hideCmdDropdown(): void {
  const dd = $('cmdDropdown');
  if (dd) dd.classList.remove('open');
  _cmdSelectedIdx = -1;
}

function navigateCmdDropdown(dir: number): void {
  const dd = $('cmdDropdown');
  if (!dd) return;
  const items = dd.querySelectorAll('.cmd-item');
  if (!items.length) return;
  items.forEach(el => el.classList.remove('selected'));
  _cmdSelectedIdx += dir;
  if (_cmdSelectedIdx < 0) _cmdSelectedIdx = items.length - 1;
  if (_cmdSelectedIdx >= items.length) _cmdSelectedIdx = 0;
  items[_cmdSelectedIdx].classList.add('selected');
}

function selectCmdDropdownItem(): void {
  const dd = $('cmdDropdown');
  if (!dd) return;
  const items = dd.querySelectorAll('.cmd-item');
  if (_cmdSelectedIdx >= 0 && _cmdSelectedIdx < items.length) {
    const item = items[_cmdSelectedIdx] as unknown as HTMLElement;
    const ev = new MouseEvent('mousedown', { bubbles: true, cancelable: true });
    Object.defineProperty(ev, 'preventDefault', { value: () => {} });
    item.dispatchEvent(ev);
  } else if (items.length === 1) {
    const ev = new MouseEvent('mousedown', { bubbles: true, cancelable: true });
    Object.defineProperty(ev, 'preventDefault', { value: () => {} });
    (items[0] as unknown as HTMLElement).dispatchEvent(ev);
  }
  hideCmdDropdown();
}

// Theme application stub (actual implementation is in ui.ts)
function _applyTheme(themeName: string): void {
  document.documentElement.dataset.theme = themeName;
}

export { COMMANDS, PASSTHROUGH, AGENT_INFO, loadCommands, parseCommand, executeCommand, getMatchingCommands, showCmdDropdown, hideCmdDropdown, navigateCmdDropdown, selectCmdDropdownItem };
