import type { Person } from './types';

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

export const COMMANDS = [
  'me',
  'notice',
  'msg',
  'join',
  'leave',
  'invite',
  'disinvite',
  'kick',
  'ban',
  'unban',
  'powerlevel',
  'mute',
  'unmute',
  'ignore',
  'unignore',
  'delete',
  'roomnick',
  'globalnick',
  'roomavatar',
  'globalavatar'
] as const;
export type CommandName = (typeof COMMANDS)[number];

/** A leading `/word`, or null for plain text. `//text` sends `/text` as a message. */
export function parseCommand(text: string): ParsedCommand | null {
  const match = /^\/([a-z]+)(?:\s+([\s\S]*))?$/i.exec(text.trim());
  if (!match) return null;
  return { name: match[1].toLowerCase(), args: (match[2] ?? '').trim() };
}

export function isKnownCommand(name: string): name is CommandName {
  return (COMMANDS as readonly string[]).includes(name);
}

export function unescapeCommand(text: string): string {
  return text.startsWith('//') ? text.slice(1) : text;
}

export type Target = { user_id: string; rest: string } | { error: string };

/** The user an argument string starts with, by matrix id or by a known display name. */
export function splitTarget(args: string, people: Person[]): Target {
  if (!args) return { error: 'Name someone' };
  if (args.startsWith('@')) {
    const [id, ...rest] = args.split(/\s+/);
    if (/^@[^:\s]+:\S+$/.test(id)) return { user_id: id, rest: rest.join(' ') };
  }
  const lower = args.toLowerCase();
  let best: Person[] = [];
  let bestLength = 0;
  for (const person of people) {
    const name = person.name.toLowerCase();
    if (!name || name.length < bestLength) continue;
    if (lower !== name && !lower.startsWith(`${name} `)) continue;
    if (name.length > bestLength) {
      best = [person];
      bestLength = name.length;
    } else {
      best.push(person);
    }
  }
  const ids = [...new Set(best.map((p) => p.user_id))];
  if (ids.length === 0) return { error: `No one here is called ${args.split(/\s+/)[0]}` };
  if (ids.length > 1) return { error: `Several people are called ${best[0].name}, use their @id` };
  return { user_id: ids[0], rest: args.slice(bestLength).trim() };
}

export const COMMAND_USAGE: Record<CommandName, string> = {
  me: '<action>',
  notice: '<text>',
  msg: '<user> [message]',
  join: '<room>',
  leave: '',
  invite: '<user>',
  disinvite: '<user> [reason]',
  kick: '<user> [reason]',
  ban: '<user> [reason]',
  unban: '<user> [reason]',
  powerlevel: '<user> <level>',
  mute: '<user>',
  unmute: '<user>',
  ignore: '<user>',
  unignore: '<user>',
  delete: '[reason]',
  roomnick: '[name]',
  globalnick: '<name>',
  roomavatar: '[mxc]',
  globalavatar: '[mxc]'
};

/** The command name being typed when the caret sits in a leading `/word`. */
export function commandQueryAt(text: string, caret: number): string | null {
  const match = /^\/([a-z]*)$/i.exec(text.slice(0, caret));
  return match ? match[1].toLowerCase() : null;
}

export function matchCommands(prefix: string): CommandName[] {
  return COMMANDS.filter((name) => name.startsWith(prefix));
}
