import type { Command } from 'commander';

const ROOT_ALIASES = ['lobe', 'lobehub'];
const HELP_COMMAND_NAME = 'help';

interface RoffDefinition {
  description: string;
  term: string;
}

const FILE_ENTRIES = [
  {
    description: 'Encrypted access and refresh tokens.',
    path: '~/.lobehub/credentials.json',
  },
  {
    description: 'CLI settings such as server and gateway URLs.',
    path: '~/.lobehub/settings.json',
  },
  {
    description: 'Background daemon PID file.',
    path: '~/.lobehub/daemon.pid',
  },
  {
    description: 'Background daemon status metadata.',
    path: '~/.lobehub/daemon.status',
  },
  {
    description: 'Background daemon log output.',
    path: '~/.lobehub/daemon.log',
  },
] as const;

const EXAMPLES = [
  {
    command: 'lh login',
    description: 'Start interactive login in the browser.',
  },
  {
    command: 'lh connect --daemon',
    description: 'Start the device gateway connection in the background.',
  },
  {
    command: 'lh search -q "gpt-5"',
    description: 'Search local resources for a query.',
  },
  {
    command: 'lh generate text "Write release notes"',
    description: 'Generate text from a prompt.',
  },
  {
    command: 'lh man generate',
    description: 'Show the built-in manual for the generate command group.',
  },
] as const;

export function generateRootManPage(program: Command, version: string) {
  const help = program.createHelp();
  const commands = getVisibleCommands(program).map((command) => ({
    description: formatCommandDescription(help.subcommandDescription(command), command.aliases()),
    term: command.name(),
  }));
  const options = help.visibleOptions(program).map((option) => ({
    description: help.optionDescription(option),
    term: help.optionTerm(option),
  }));

  const lines = [
    '.\\" Code generated by `npm run man:generate`; DO NOT EDIT.',
    '.\\" Manual command details come from the Commander command tree.',
    `.TH LH 1 "" "${escapeRoff(`@lobehub/cli ${version}`)}" "User Commands"`,
    '.SH NAME',
    `lh \\- ${escapeRoff(program.description() || 'LobeHub CLI')}`,
    '.SH SYNOPSIS',
    ...formatSynopsisLines(),
    '.SH DESCRIPTION',
    escapeRoff(
      `${program.name()} is the command-line interface for LobeHub. It provides authentication, device gateway connectivity, content generation, resource search, and management commands for agents, files, models, providers, plugins, knowledge bases, threads, topics, and related resources.`,
    ),
    '.PP',
    'For command-specific manuals, use the built-in manual command:',
    '.PP',
    '.RS',
    '.B lh man',
    '[\\fICOMMAND\\fR]...',
    '.RE',
    '.SH COMMANDS',
    ...formatDefinitionSection(commands, 'B'),
    '.SH OPTIONS',
    ...formatDefinitionSection(options, 'B'),
    '.SH FILES',
    ...FILE_ENTRIES.flatMap((entry) => [
      '.TP',
      `.I ${escapeRoff(entry.path)}`,
      escapeRoff(entry.description),
    ]),
    '.PP',
    'The base directory can be overridden with the',
    '.B LOBEHUB_CLI_HOME',
    'environment variable.',
    '.SH EXAMPLES',
    ...EXAMPLES.flatMap((example) => [
      '.TP',
      `.B ${escapeRoff(example.command)}`,
      escapeRoff(example.description),
    ]),
    '.SH SEE ALSO',
    '.BR lobe (1),',
    '.BR lobehub (1)',
  ];

  return `${lines.join('\n')}\n`;
}

export function generateAliasManPage(target: string) {
  return `.so man1/${target}.1\n`;
}

function formatSynopsisLines() {
  return ['lh', ...ROOT_ALIASES]
    .flatMap((binary) => [`.B ${binary}`, '[\\fIOPTION\\fR]...', '[\\fICOMMAND\\fR]', '.br'])
    .slice(0, -1);
}

function getVisibleCommands(command: Command) {
  return command
    .createHelp()
    .visibleCommands(command)
    .filter((subcommand) => subcommand.name() !== HELP_COMMAND_NAME);
}

function formatCommandDescription(description: string, aliases: string[]) {
  if (aliases.length === 0) return description;

  return `${description} Alias: ${aliases.join(', ')}.`;
}

function formatDefinitionSection(items: RoffDefinition[], macro: 'B' | 'I') {
  return items.flatMap((item) => [
    '.TP',
    `.${macro} ${escapeRoff(item.term)}`,
    escapeRoff(item.description),
  ]);
}

function escapeRoff(value: string) {
  return value.replaceAll('\\', '\\\\').replaceAll('-', '\\-');
}
