const chalk = require('chalk')
const { console, process } = require('./src/globals')
const { addDashes, camelCaseFromDash } = require('./src/helpers')

module.exports = (argv, { options, parameters }) => {
  try {
    argv = argv.slice(0)
    const args = {}

    options = options.reduce((options, definition) => {
      definition = Object.assign({}, definition, { property: camelCaseFromDash(definition.key) })

      options.push(definition)

      if (definition.alias) {
        options.push(Object.assign({}, definition, { key: definition.alias, alias: true }))
      }

      return options
    }, [])

    parameters = parameters.reduce((parameters, definition) => {
      definition = Object.assign({}, definition, { property: camelCaseFromDash(definition.key) })

      parameters.push(definition)

      return parameters
    }, [])

    let afterDashDash = []
    const indexOfDashDash = argv.indexOf('--')

    if (indexOfDashDash > -1) {
      afterDashDash = argv.slice(indexOfDashDash + 1)

      argv = argv.slice(0, indexOfDashDash)
    }

    argv = argv.reduce((argv, arg) => {
      if (arg !== '-' && arg.startsWith('-') && !arg.startsWith('--')) {
        if (arg.indexOf('=') > -1) {
          argv.push('-' + arg.substr(arg.indexOf('=') - 1))

          arg = arg.substring(1, arg.indexOf('=') - 1)
        } else {
          arg = arg.substr(1)
        }

        argv = argv.concat(arg.split('').map((arg) => '-' + arg))
      } else {
        argv.push(arg)
      }

      return argv
    }, [])

    const toBeDeleted = []

    for (let i = 0; i < argv.length; i++) {
      for (const definition of options) {
        const search = addDashes(definition.key)
        const property = definition.property
        const vals = []
        const isSearch = argv[i] === search
        const nextIsValid = argv[i + 1] != null && (!argv[i + 1].startsWith('-') || argv[i + 1].startsWith('---') || argv[i + 1] === '-')
        const isBoolean = definition.type == null
        const hasValue = argv[i].startsWith(search + '=')

        if (isSearch && !isBoolean && nextIsValid) {
          vals.push(argv[i + 1])

          toBeDeleted.push(i + 1)

          toBeDeleted.push(i)
        } else if (isSearch && isBoolean && !nextIsValid) {
          vals.push(true)

          toBeDeleted.push(i)
        } else if (!isBoolean && hasValue) {
          vals.push(argv[i].substr(argv[i].indexOf('=') + 1))

          toBeDeleted.push(i)
        } else if (isSearch && !isBoolean) {
          throw new Error(addDashes(definition.key) + ' is not a boolean and requires a value')
        } else if ((isSearch || hasValue) && isBoolean) {
          throw new Error(addDashes(definition.key) + ' is a boolean and does not accept a value')
        }

        if (vals != null && vals.length) {
          if (definition.multiple === true) {
            args[property] = args[property] != null ? args[property].concat(vals) : vals
          } else if (args[property] != null) {
            throw new Error(addDashes(definition.key) + ' does not accept multiple values')
          } else {
            args[property] = vals.pop()
          }
        }
      }
    }

    for (const definition of options.filter((option) => options.alias !== true)) {
      const property = definition.property

      if (args[property] == null) {
        if (definition.type != null) {
          const _default = definition.type()

          if (_default != null) {
            args[property] = _default
          } else if (definition.required === true && args['help'] !== true) {
            throw new Error(addDashes(definition.key) + ' is required')
          }
        } else if (definition.required !== true) {
          args[property] = false
        } else if (definition.required === true && args['help'] !== true) {
          throw new Error(addDashes(definition.key) + ' is required')
        }
      } else if (definition.type != null) {
        args[property] = definition.type(args[property])
      }
    }

    argv = argv.reduce((argv, arg, i) => {
      if (!toBeDeleted.includes(i)) {
        argv.push(arg)
      }

      return argv
    }, [])

    for (const arg of argv) {
      if (arg.startsWith('-') && !arg.startsWith('---')) {
        throw new Error('unknown option ' + arg.split('=')[0])
      }
    }

    const remainder = argv.concat(afterDashDash).filter((arg) => arg !== '')

    const hasMultiple = parameters.filter((definition) => definition.multiple).length > 0

    if (!hasMultiple && remainder.length > parameters.length) {
      throw new Error('too many arguments')
    }

    let remainingKeys = parameters.length

    for (const definition of parameters) {
      const property = definition.property
      remainingKeys -= 1

      if (!remainder.length) {
        if (definition.type != null) {
          const _default = definition.type()

          if (_default != null) {
            args[property] = _default
          } else if (definition.required === true && args['help'] !== true) {
            throw new Error(definition.key + ' is required')
          }
        } else if (definition.required === true && args['help'] !== true) {
          throw new Error(definition.key + ' is required')
        }
      } else if (definition.multiple === true) {
        args[property] = remainder.splice(0, remainder.length - remainingKeys)

        if (definition.type != null) {
          args[property] = definition.type(args[property])
        }
      } else if (definition.type != null) {
        args[property] = definition.type(remainder.shift())
      } else {
        args[property] = remainder.shift()
      }
    }

    return args
  } catch (error) {
    process.exitCode = 1

    console.error(chalk.red(error.message))
  }
}

@@ -1795,16 +1795,83 @@
  search%0A
+        const isSearchWithValue = argv%5Bi%5D.startsWith(search + '=')%0A
         
@@ -2053,66 +2053,8 @@
 null
-%0A        const hasValue = argv%5Bi%5D.startsWith(search + '=')
 %0A%0A  
@@ -2067,33 +2067,32 @@
 if (isSearch && 
-!
 isBoolean && nex
@@ -2088,23 +2088,8 @@
 lean
- && nextIsValid
 ) %7B%0A
@@ -2112,54 +2112,12 @@
 ush(
-argv%5Bi + 1%5D)%0A%0A          toBeDeleted.push(i + 1
+true
 )%0A%0A 
@@ -2180,61 +2180,103 @@
  && 
-isBoolean && !nextIsValid) %7B%0A          vals.push(true
+nextIsValid && !isBoolean) %7B%0A          vals.push(argv%5Bi + 1%5D)%0A%0A          toBeDeleted.push(i + 1
 )%0A%0A 
@@ -2327,30 +2327,39 @@
 if (
-!
 is
-Boolean && hasValue
+SearchWithValue && !isBoolean
 ) %7B%0A
@@ -2610,17 +2610,16 @@
 else if 
-(
 (isSearc
@@ -2623,21 +2623,17 @@
 arch
- %7C%7C has
+With
 Value
-)
  && 

Fix booleans