_ = require("lodash")
Promise = require("bluebird")
debug = require("debug")("cypress:server:task")
plugins = require("./plugins")

docsUrl = "https://on.cypress.io/api/task"

throwKnownError = (message, props = {}) ->
  err = new Error(message)
  _.extend(err, props, { isKnownError: true })
  throw err

module.exports = {
  run: (pluginsFilePath, options) ->
    debug("run task", options.task, "with arg", options.arg)

    fileAndDocsUrl = "\n\nFix this in your plugins file here:\n#{pluginsFilePath}\n\n#{docsUrl}"

    Promise
    .try ->
      if not plugins.has("task")
        debug("'task' event is not registered")
        throwKnownError("The 'task' event has not been registered in the plugins file. You must register it before using cy.task()#{fileAndDocsUrl}")

      plugins.execute("task", options.task, options.arg)
    .then (result) ->
      if result is "__cypress_unhandled__"
        debug("task is unhandled")
        return plugins.execute("_get:task:keys").then (keys) ->
          throwKnownError("The task '#{options.task}' was not handled in the plugins file. The following tasks are registered: #{keys.join(", ")}#{fileAndDocsUrl}")

      if result is undefined
        debug("result is undefined")
        return plugins.execute("_get:task:body", options.task).then (body) ->
          handler = if body then "\n\nThe task handler was:\n\n#{body}" else ""
          throwKnownError("The task '#{options.task}' returned undefined. You must return a promise, a value, or null to indicate that the task was handled.#{handler}#{fileAndDocsUrl}")

      debug("result is:", result)
      return result
    .timeout(options.timeout)
    .catch Promise.TimeoutError, ->
      debug("timed out after #{options.timeout}ms")
      plugins.execute("_get:task:body", options.task).then (body) ->
        err = new Error("The task handler was:\n\n#{body}#{fileAndDocsUrl}")
        err.timedOut = true
        throw err
}
