http = require 'http'
_ = 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 JSON.parse 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
        #压缩模式
        compressObj = {}
        propertyName = []
        propertyVal = []
        keys = _.filter _.keys(@option.data), (v) ->
          v not in ['action', 'timestamp', 'appkey', 'sign', 'isCompress', 'isdebug']
        _.each keys, (k) =>
          compressObj[k] = @option.data[k]
          propertyName.push k
          propertyVal.push @option.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 = require('querystring').stringify @option.data
      delete @option.data
    else if @option.method is 'POST' and _.has @option, 'stream'
      reqContent = @option.stream
      delete @option.stream
    @reqContent = reqContent
    #console.log "reqContent: #{@reqContent}"

    if @option.method is 'GET' and @reqContent isnt ''
      @option.path += "?#{@reqContent}"

  request: ->
    req = null
    timeout = C.timeout or 8
    reqAsync = new Promise (resolve, reject) =>
      req = http.request @option, (res) =>
        resContent = ''
        if res.statusCode is 200
          res.on 'data', (content1) ->
            resContent += content1
          .on 'end', ->
            resolve resContent
        else
          reject new errors.HttpError "Status Code: #{res.statusCode}", @version, @action

      req.on 'error', (e) =>
        reject new errors.HttpError e.message, @version, @action

      if @option.method is 'POST'
        #console.log @reqContent
        req.write @reqContent

      req.end();
    reqAsync.timeout timeout * 1000
    .catch Promise.TimeoutError, =>
      req.abort()
      Promise.reject new errors.HttpTimeoutError "连接超时，超时限制#{timeout}秒", @version, @action

module.exports = Request