import { program } from 'commander'

import color from 'ansis'

import pkg from '../../package.json'
import { SHA } from './sha'
import {
  cronList,
  cronLog,
  cronRun,
  cronStatus,
  envList,
  envRm,
  envSet,
  getApp,
  infoApp,
  listApps,
  logApp,
  metricsApp,
  newApp,
  openApp,
  perfToggle,
  renameApp,
  restartApp,
  rmApp,
  shareApp,
  startApp,
  stopApp,
  unshareApp,
} from './commands'

program
  .name('toes')
  .version(`v${pkg.version}-${SHA}`, '-v, --version')
  .addHelpText('beforeAll', (ctx) => {
    if (ctx.command === program) {
      return color.bold.cyan('🐾 Toes') + color.gray(' - personal web appliance\n')
    }
    return ''
  })
  .addHelpCommand(false)
  .configureOutput({
    writeOut: (str) => {
      const colored = str
        .replace(/^([A-Z][\w ]*:)/gm, color.yellow('$1'))
      process.stdout.write(colored)
    },
  })

program
  .command('version', { hidden: true })
  .action(() => console.log(program.version()))

// Apps

program
  .command('status')
  .helpGroup('Apps:')
  .description('Show status of all apps, or details for a specific app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .option('-t, --tools', 'show only tools')
  .option('-a, --apps', 'show only apps (exclude tools)')
  .action((name?: string, options?: { apps?: boolean; tools?: boolean }) => {
    if (name) return infoApp(name)
    return listApps(options ?? {})
  })

program
  .command('list', { hidden: true })
  .option('-t, --tools', 'show only tools')
  .option('-a, --apps', 'show only apps (exclude tools)')
  .action(listApps)

program
  .command('info', { hidden: true })
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(infoApp)

program
  .command('new')
  .helpGroup('Apps:')
  .description('Create a new toes app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .option('--ssr', 'SSR template with pages directory (default)')
  .option('--bare', 'minimal template with no pages')
  .option('--spa', 'single-page app with client-side rendering')
  .action(newApp)

program
  .command('get')
  .helpGroup('Apps:')
  .description('Clone an app from the server')
  .argument('<name>', 'app name')
  .argument('[directory]', 'target directory (defaults to app name)')
  .action(getApp)

program
  .command('open')
  .helpGroup('Apps:')
  .description('Open an app in browser')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(openApp)

program
  .command('rename')
  .helpGroup('Apps:')
  .description('Rename an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .argument('<new-name>', 'new app name')
  .action(renameApp)

program
  .command('rm')
  .helpGroup('Apps:')
  .description('Remove an app from the server')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(rmApp)

// Lifecycle

program
  .command('start')
  .helpGroup('Lifecycle:')
  .description('Start an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(startApp)

program
  .command('stop')
  .helpGroup('Lifecycle:')
  .description('Stop an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(stopApp)

program
  .command('restart')
  .helpGroup('Lifecycle:')
  .description('Restart an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(restartApp)

program
  .command('share')
  .helpGroup('Lifecycle:')
  .description('Share an app via public tunnel')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(shareApp)

program
  .command('unshare')
  .helpGroup('Lifecycle:')
  .description('Stop sharing an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(unshareApp)

program
  .command('logs')
  .helpGroup('Lifecycle:')
  .description('Show logs for an app')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .option('-f, --follow', 'follow log output')
  .option('-d, --date <date>', 'show logs from a specific date (YYYY-MM-DD)')
  .option('-s, --since <duration>', 'show logs since duration (e.g., 1h, 2d)')
  .option('-g, --grep <pattern>', 'filter logs by pattern')
  .action(logApp)

program
  .command('log', { hidden: true })
  .argument('[name]', 'app name (uses current directory if omitted)')
  .option('-f, --follow', 'follow log output')
  .option('-d, --date <date>', 'show logs from a specific date (YYYY-MM-DD)')
  .option('-s, --since <duration>', 'show logs since duration (e.g., 1h, 2d)')
  .option('-g, --grep <pattern>', 'filter logs by pattern')
  .action(logApp)

program
  .command('metrics')
  .helpGroup('Lifecycle:')
  .description('Show CPU, memory, and disk metrics for apps')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .action(metricsApp)

const cron = program
  .command('cron')
  .helpGroup('Lifecycle:')
  .description('Manage cron jobs')
  .argument('[app]', 'app name (list jobs for specific app)')
  .action(cronList)

cron
  .command('log')
  .description('Show cron job logs')
  .argument('[target]', 'app name or job (app:name)')
  .option('-f, --follow', 'follow log output')
  .action(cronLog)

cron
  .command('status')
  .description('Show detailed status for a job')
  .argument('<job>', 'job identifier (app:name)')
  .action(cronStatus)

cron
  .command('run')
  .description('Run a job immediately')
  .argument('<job>', 'job identifier (app:name)')
  .action(cronRun)

// Config

const env = program
  .command('env')
  .helpGroup('Config:')
  .description('Manage environment variables')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .option('-g, --global', 'manage global variables shared by all apps')
  .action(envList)

env
  .command('set')
  .description('Set an environment variable')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .argument('<key>', 'variable name')
  .argument('[value]', 'variable value (or use KEY=value format)')
  .option('-g, --global', 'set a global variable shared by all apps')
  .action(envSet)

env
  .command('rm')
  .description('Remove an environment variable')
  .argument('[name]', 'app name (uses current directory if omitted)')
  .argument('<key>', 'variable name to remove')
  .option('-g, --global', 'remove a global variable')
  .action(envRm)

program
  .command('perf')
  .helpGroup('Config:')
  .description('Toggle request timing for proxied app requests')
  .argument('[on|off|status]', 'enable, disable, or check status (toggles if omitted)')
  .action(perfToggle)

// Shell

program
  .command('shell')
  .description('Interactive shell')
  .action(async () => {
    const { shell } = await import('./shell')
    await shell()
  })

// Hide and disable commands that don't work over SSH
if (process.env.USER === 'cli') {
  const disabled = ['shell', 'get', 'open']
  for (const name of disabled) {
    const cmd = program.commands.find((c) => c.name() === name)
    if (!cmd) continue
    cmd.helpInformation = () => ''
    ;(cmd as any)._hidden = true
    cmd.action(() => {
      console.error(`"${name}" is not available over SSH`)
      process.exit(1)
    })
  }
}

export { program }
