local cjson = require "cjson"
local sandbox = require "kong.tools.sandbox".sandbox
local kong_meta = require "kong.meta"
local get_host_name = kong.node.get_hostname


local kong = kong
local ngx = ngx
local date = os.date
local tostring = tostring
local timer_at = ngx.timer.at
local udp = ngx.socket.udp
local concat = table.concat
local insert = table.insert


local HOSTNAME = get_host_name()
local SENDER_NAME = "kong"
local LOG_LEVELS = {
  debug = 7,
  info = 6,
  notice = 5,
  warning = 4,
  err = 3,
  crit = 2,
  alert = 1,
  emerg = 0
}


local function merge(conf, message, pri)
  local tags_list = conf.tags
  local tags = {}
  for i = 1, #tags_list do
    insert(tags, "tag=" .. '"' .. tags_list[i] .. '"')
  end

  local udp_message = {
    "<" .. pri .. ">1",
    date("!%Y-%m-%dT%XZ"),
    HOSTNAME,
    SENDER_NAME,
    "-",
    "-",
    "[" .. conf.key .. "@41058", concat(tags, " ") .. "]",
    cjson.encode(message)
  }

  return concat(udp_message, " ")
end


local function send_to_loggly(conf, message, pri)
  local host = conf.host
  local port = conf.port
  local timeout = conf.timeout

  local udp_message = merge(conf, message, pri)

  local sock = udp()

  sock:settimeout(timeout)

  local ok, err = sock:setpeername(host, port)
  if not ok then
    kong.log.err("failed to connect to ", host, ":", tostring(port), ": ", err)
    sock:close()
    return
  end

  local ok, err = sock:send(udp_message)
  if not ok then
    kong.log.err("failed to send data to ", host, ":", tostring(port), ": ", err)
  end

  local ok, err = sock:close()
  if not ok then
    kong.log.err("failed to close connection from ", host, ":", tostring(port), ": ", err)
    return
  end
end

local function decide_severity(conf, severity, message)
  if LOG_LEVELS[severity] > LOG_LEVELS[conf.log_level] then
    return
  end

  local pri = 8 + LOG_LEVELS[severity]
  return send_to_loggly(conf, message, pri)
end

local is_html = nil

local function log(premature, conf, message)
  if premature then
    return
  end

  if is_html == nil then
    is_html = ngx.config.subsystem == "http"
  end

  if is_html then
    if message.response.status >= 500 then
      return decide_severity(conf, conf.server_errors_severity, message)
    end

    if message.response.status >= 400 then
      return decide_severity(conf, conf.client_errors_severity, message)
    end
  end

  return decide_severity(conf, conf.successful_severity, message)
end


local LogglyLogHandler = {
  PRIORITY = 6,
  VERSION = kong_meta.version,
}


function LogglyLogHandler:log(conf)
  if conf.custom_fields_by_lua then
    local set_serialize_value = kong.log.set_serialize_value
    for key, expression in pairs(conf.custom_fields_by_lua) do
      set_serialize_value(key, sandbox(expression)())
    end
  end

  local message = kong.log.serialize()

  local ok, err = timer_at(0, log, conf, message)
  if not ok then
    kong.log.err("failed to create timer: ", err)
  end
end


return LogglyLogHandler
