request = require 'superagent'
_ = require 'lodash'
utils = require 'utility'
Promise = require 'bluebird'
moment = require 'moment'
errors = require '../errors/index'
Tools = require './Tools'

class Request extends Tools
  constructor: ->
    @option = {
      headers:
        'Content-Type': 'application/x-www-form-urlencoded'
    }
    @middlewares = []

  #处理最终的返回数据，api继承该类后重新该方法得到需要输出的格式
  dispose_data: (data) ->
    Promise.resolve data

  get_middlwares: (name) ->
    _.filter @middlewares, (middleware) ->
      _.hasIn middleware, name

  set_method: (method) ->
    @action = @params.action
    @option.method = method

    Promise.map @get_middlwares('start'), (middleware) =>
      middleware.start @
    .then =>
      @make_request()
      Promise.map @get_middlwares('afterMakeRequest'), (middleware) =>
        middleware.afterMakeRequest @
    .then =>
      @request()
    .then (data) =>
      @dispose_data data
    .catch (err) =>
      Promise.map @get_middlwares('error'), (middleware) =>
        middleware.error @, err
      .then ->
        Promise.reject err
    .finally =>
      Promise.map @get_middlwares('end'), (middleware) =>
        middleware.end @

  post: (@params) ->
    @set_method 'POST'

  get: (@params) ->
    @set_method 'GET'

  #生成request所需要的参数，api接口继承该类后重写该方法并super
  make_request: ->
    reqContent = ''
    if typeof @option.data isnt 'undefined'
      if @option.data.isCompress is on
        propertyName = _.filter _.keys(@option.data), (v) ->
          v not in ['action', 'timestamp', 'appkey', 'sign', 'isCompress', 'isdebug']
        propertyVal = _.map propertyName, (k) ->
          @options.data[k]

        sign_str = "#{@option.data.appkey}|#{@option.data.timestamp}|#{C.api.v3.appsecret}|"
        json = {
          reqs: [{
            act: @option.data.action
            prms: [{
              n: 'qry'
              f: propertyName.join ','
              v: propertyVal.join ','
            }]
          }]
        }

        json_str = JSON.stringify json

        sign_source = sign_str + json_str
        sign_source = sign_source.toLowerCase()
        sign = utils.md5(sign_source).toLowerCase()

        reqContent = "##{@option.data.appkey}|#{@option.data.timestamp}||#{sign}$#{json_str}"
      else
        reqContent = @option.data
    else if @option.method is 'POST' and _.has @option, 'stream'
      reqContent = @option.stream
    @reqContent = reqContent

  request: ->
    if @option.method is 'POST'
      req = request.post
    else
      req = request.get

    timeout = C.timeout or 8

    new Promise (resolve, reject) =>
      url = "http://#{@option.hostname}:#{@option.port}#{@option.path}"
      req = req url
      .set @option.headers
      .timeout timeout * 1000
      .set('Accept', 'application/json')

      if @option.method is 'POST'
        req = req.send @reqContent
      else
        req = req.query @reqContent

      req.end (err, res) =>
        if err
          reject err
          return
        if res.ok
          resolve res.body
        else
          reject new errors.ApiError '解析错误', res.text, @version, @action

module.exports = Request