const { next } = require('hooter/effects')
const Command = require('./Command')
const Option = require('./Option')


function buildConfig(rootCommand) {
  let commands = [rootCommand.getConfig()]
  let options = rootCommand.options.map((option) => option.getConfig())

  if (rootCommand.commands) {
    rootCommand.commands.forEach((command) => {
      let config = buildConfig(command)
      commands = commands.concat(config.commands)
      options = options.concat(config.options)
    })
  }

  return { commands, options }
}


class ExecutableCommand extends Command {
  handle(command, handler) {
    if (arguments.length === 2) {
      if (typeof command !== 'string') {
        throw new Error('A command name must be a string')
      }

      if (!/^[a-z0-9 _-]+$/.test(command)) {
        throw new Error(
          'A command name can only contain letters, numbers, underscores, hyphens and spaces'
        )
      }

      if (command.charAt(0) === '-') {
        throw new Error(
          'A hyphen is not allowed as the first character of a command name'
        )
      }

      command = this.getFullName().concat(command.split(' '))
    } else {
      handler = command
      command = this.getFullName()
    }

    if (typeof handler !== 'function') {
      throw new Error('A handler must be a function')
    }

    this.lifecycle.hook('handle', function* (
      _command, context, ...args
    ) {
      let { outputName, options } = _command

      if (outputName.length === command.length &&
          outputName.every((n, i) => command[i] === n)) {
        context = yield handler(options, context, ...args)
      }

      return yield next(_command, context, ...args)
    })

    return this
  }

  execute(command, options) {
    if (typeof command !== 'string') {
      options = command
      command = null
    }

    let name = this.getFullName()

    if (command) {
      name = name.concat(command.split(' '))
    }

    return this.lifecycle.toot('execute', [{ name, options }])
  }

  start() {
    if (!this.lifecycle || this.parent) {
      throw new Error('start() can only be used on the default command (the app)')
    }

    return this.lifecycle.tootWith(
      'start',
      (config) => config,
      buildConfig(this)
    )
  }
}

ExecutableCommand.Option = Option

module.exports = ExecutableCommand

@@ -1937,38 +1937,47 @@
  '))%0A    %7D%0A%0A    
-return
+let lifecycle =
  this.lifecycle.
@@ -1971,24 +1971,52 @@
 is.lifecycle
+%0A    return lifecycle%0A      
 .toot('execu
@@ -2047,37 +2047,120 @@
 )%0A  
-%7D%0A%0A  start() %7B%0A    if (!this.
+    .catch((err) =%3E lifecycle.toot('error', err))%0A  %7D%0A%0A  start() %7B%0A    let lifecycle = this.lifecycle%0A%0A    if (!
 life
@@ -2281,20 +2281,64 @@
     
+try %7B%0A      let config = buildConfig(this)%0A      
 return 
-this.
 life
@@ -2356,23 +2356,16 @@
 ith(
-%0A      
 'start',
 %0A   
@@ -2364,16 +2364,11 @@
 rt',
-%0A     
  (
+_
 conf
@@ -2378,45 +2378,85 @@
  =%3E 
+_
 config,
-%0A      buildConfig(this
+ config)%0A    %7D catch (err) %7B%0A      lifecycle.toot('error', err
 )%0A    
-)
+%7D
 %0A  %7D

Add error handling to ExecutableCommand