import parse from 'try-json-parse'
import handleAuth from './auth'
import KiteError from './error'
import { Event } from '../constants'

export default function handleIncomingMessage(proto, message) {
  this.emit(Event.debug, `Receiving: ${message}`)

  if (typeof message === 'string') {
    message = parse(message)
  }

  const req = message

  if (req == null) {
    this.emit(Event.warning, new KiteError(`Invalid payload! (${message})`))
    return
  }

  if (!isKiteReq(req)) {
    this.emit(Event.debug, 'Handling a normal dnode message')
    return proto.handle(req)
  }

  const { links, method, callbacks } = req
  const { authentication: auth, responseCallback } = parseKiteReq(req)

  this.emit(Event.debug, 'Authenticating request')

  return handleAuth(this, method, auth, this.key)
    .then(token => {
      this.emit(Event.debug, 'Authentication passed')

      // set this as the current token for the duration of the synchronous
      // method call.
      // NOTE: this mechanism may be changed at some point in the future.
      this.currentToken = token

      try {
        proto.handle(req)
      } catch (err) {
        this.emit(Event.debug, 'Error processing request', err)
        proto.handle(
          getTraceReq({
            kite: this.getKiteInfo(),
            err,
            responseCallback,
            links,
            callbacks,
          })
        )
      }

      this.currentToken = null
      return null
    })
    .catch(err => {
      this.emit(Event.debug, 'Authentication failed', err)

      proto.handle(
        getTraceReq({
          kite: this.getKiteInfo(),
          err,
          responseCallback,
          links,
          callbacks,
        })
      )
    })
}

const isKiteReq = req =>
  req.arguments.length &&
  req.arguments[0] &&
  req.arguments[0].responseCallback &&
  req.arguments[0].withArgs

const getTraceReq = o => {
  return {
    method: 'kite.echo',
    arguments: [
      {
        withArgs: [{ error: o.err }],
        responseCallback: o.responseCallback,
        kite: o.kite,
      },
    ],
    links: o.links,
    callbacks: o.callbacks,
  }
}

const parseKiteReq = req => req.arguments[0]

const isResponseCallback = callback =>
  Array.isArray(callback) && callback.join('.') === '0.responseCallback'

const mungeCallbacks = (callbacks, n) => {
  // FIXME: this is an ugly hack; there must be a better way to implement it:

  for (let key of Object.keys(callbacks || {})) {
    const callback = callbacks[key]
    if (isResponseCallback(callback)) {
      callbacks[key] = [n]
    }
    if (callback[1] === 'withArgs') {
      // since we're rewriting the protocol for the withArgs case,
      // we need to remove everything up to withArgs
      callbacks[key] = callback.slice(2)
    }
  }

  return callbacks
}

const toProtoReq = ({
  method,
  withArgs,
  responseCallback,
  links,
  callbacks,
}) => {
  if (!Array.isArray(withArgs)) {
    withArgs = [withArgs]
  }

  mungeCallbacks(callbacks, withArgs.length)

  return {
    method,
    arguments: [...Array.from(withArgs), responseCallback],
    links,
    callbacks,
  }
}

@@ -2179,951 +2179,4 @@
 %5B0%5D%0A
-%0Aconst isResponseCallback = callback =%3E%0A  Array.isArray(callback) && callback.join('.') === '0.responseCallback'%0A%0Aconst mungeCallbacks = (callbacks, n) =%3E %7B%0A  // FIXME: this is an ugly hack; there must be a better way to implement it:%0A%0A  for (let key of Object.keys(callbacks %7C%7C %7B%7D)) %7B%0A    const callback = callbacks%5Bkey%5D%0A    if (isResponseCallback(callback)) %7B%0A      callbacks%5Bkey%5D = %5Bn%5D%0A    %7D%0A    if (callback%5B1%5D === 'withArgs') %7B%0A      // since we're rewriting the protocol for the withArgs case,%0A      // we need to remove everything up to withArgs%0A      callbacks%5Bkey%5D = callback.slice(2)%0A    %7D%0A  %7D%0A%0A  return callbacks%0A%7D%0A%0Aconst toProtoReq = (%7B%0A  method,%0A  withArgs,%0A  responseCallback,%0A  links,%0A  callbacks,%0A%7D) =%3E %7B%0A  if (!Array.isArray(withArgs)) %7B%0A    withArgs = %5BwithArgs%5D%0A  %7D%0A%0A  mungeCallbacks(callbacks, withArgs.length)%0A%0A  return %7B%0A    method,%0A    arguments: %5B...Array.from(withArgs), responseCallback%5D,%0A    links,%0A    callbacks,%0A  %7D%0A%7D%0A

remove dirty hacks to rewrite protocol