local Queue = require "kong.tools.queue"
local statsd_logger = require "kong.plugins.datadog.statsd_logger"
local kong_meta = require "kong.meta"


local replace_dashes = require("kong.tools.string").replace_dashes


local kong     = kong
local ngx      = ngx
local null     = ngx.null
local insert   = table.insert
local gsub     = string.gsub
local pairs    = pairs
local ipairs   = ipairs


local get_consumer_id = {
  consumer_id = function(consumer)
    return consumer and replace_dashes(consumer.id)
  end,
  custom_id = function(consumer)
    return consumer and consumer.custom_id
  end,
  username = function(consumer)
    return consumer and consumer.username
  end
}


local function compose_tags(service_name, status, consumer_id, tags, conf)
  local result = {
    (conf.service_name_tag or "name") .. ":" .. service_name,
    (conf.status_tag or "status") .. ":" .. status
  }

  if consumer_id ~= nil then
    insert(result, (conf.consumer_tag or "consumer") .. ":" .. consumer_id)
  end

  if tags ~= nil then
    for _, v in pairs(tags) do
      insert(result, v)
    end
  end

  return result
end


local function send_entries_to_datadog(conf, messages)
  local logger, err = statsd_logger:new(conf)
  if err then
    kong.log.err("failed to create Statsd logger: ", err)
    return false, err
  end

  for _, message in ipairs(messages) do
    local stat_name  = {
      request_size     = "request.size",
      response_size    = "response.size",
      latency          = "latency",
      upstream_latency = "upstream_latency",
      kong_latency     = "kong_latency",
      request_count    = "request.count",
    }
    local stat_value = {
      request_size     = message.request and message.request.size,
      response_size    = message.response and message.response.size,
      latency          = message.latencies.request,
      upstream_latency = message.latencies.proxy,
      kong_latency     = message.latencies.kong,
      request_count    = 1,
    }

    for _, metric_config in pairs(conf.metrics) do
      local stat_name       = stat_name[metric_config.name]
      if stat_name == nil then
        goto continue
      end

      local stat_value      = stat_value[metric_config.name]
      local get_consumer_id = get_consumer_id[metric_config.consumer_identifier]
      local consumer_id     = get_consumer_id and get_consumer_id(message.consumer) or nil
      local tags            = compose_tags(
                                message.service and gsub(message.service.name ~= null and
                                message.service.name or message.service.host, "%.", "_") or "",
                                message.response and message.response.status or "-",
                                consumer_id, metric_config.tags, conf)

      logger:send_statsd(stat_name, stat_value,
                         logger.stat_types[metric_config.stat_type],
                         metric_config.sample_rate, tags)
      ::continue::
    end
  end

  logger:close_socket()
  return true
end


local DatadogHandler = {
  PRIORITY = 10,
  VERSION = kong_meta.version,
}

function DatadogHandler:log(conf)
  local ok, err = Queue.enqueue(
    Queue.get_plugin_params("datadog", conf),
    send_entries_to_datadog,
    conf,
    kong.log.serialize()
  )
  if not ok then
    kong.log.err("failed to enqueue log entry to Datadog: ", err)
  end
end


return DatadogHandler
