local isempty = require "table.isempty"
local mime_type = require "kong.tools.mime_type"


local kong = kong
local type = type
local match = string.match
local noop = function() end
local parse_mime_type = mime_type.parse_mime_type
local mime_type_includes = mime_type.includes
local isplitn = require("kong.tools.string").isplitn
local strip = require("kong.tools.string").strip


local _M = {}


local function iter(config_array)
  if type(config_array) ~= "table" then
    return noop
  end

  return function(config_array, i)
    i = i + 1

    local header_to_test = config_array[i]
    if header_to_test == nil then -- n + 1
      return nil
    end

    local header_to_test_name, header_to_test_value = match(header_to_test, "^([^:]+):*(.-)$")
    if header_to_test_value == "" then
      header_to_test_value = nil
    end

    return i, header_to_test_name, header_to_test_value
  end, config_array, 0
end


local function is_json_body(content_type)
  if not content_type then
    return false
  end
  local expected_media_type = { type = "application", subtype = "json" }
  for ct in isplitn(content_type, ",") do
    local t, subtype = parse_mime_type(strip(ct))
    if not t or not subtype then
      goto continue
    end
    local media_type = { type = t, subtype = subtype }
    if mime_type_includes(expected_media_type, media_type) then
      return true
    end
    ::continue::
  end

  return false
end


local function is_body_transform_set(conf)
  return not isempty(conf.add.json    ) or
         not isempty(conf.rename.json ) or
         not isempty(conf.remove.json ) or
         not isempty(conf.replace.json) or
         not isempty(conf.append.json )
end


-- export utility functions
_M.is_json_body = is_json_body
_M.is_body_transform_set = is_body_transform_set


---
--   # Example:
--   ngx.headers = header_filter.transform_headers(conf, ngx.headers)
-- We run transformations in following order: remove, rename, replace, add, append.
-- @param[type=table] conf Plugin configuration.
-- @param[type=table] ngx_headers Table of headers, that should be `ngx.headers`
-- @return table A table containing the new headers.
function _M.transform_headers(conf, headers)
  local clear_header = kong.response.clear_header
  local set_header   = kong.response.set_header
  local add_header   = kong.response.add_header

  -- remove headers
  for _, header_name in iter(conf.remove.headers) do
      clear_header(header_name)
  end

  -- rename headers(s)
  for _, old_name, new_name in iter(conf.rename.headers) do
    if headers[old_name] ~= nil and new_name then
      local value = headers[old_name]
      set_header(new_name, value)
      clear_header(old_name)
    end
  end

  -- replace headers
  for _, header_name, header_value in iter(conf.replace.headers) do
    if headers[header_name] ~= nil and header_value then
      set_header(header_name, header_value)
    end
  end

  -- add headers
  for _, header_name, header_value in iter(conf.add.headers) do
    if headers[header_name] == nil and header_value then
      set_header(header_name, header_value)
    end
  end

  -- append headers
  for _, header_name, header_value in iter(conf.append.headers) do
    add_header(header_name, header_value)
  end

  -- Removing the content-length header because the body is going to change
  if is_body_transform_set(conf) and is_json_body(headers["Content-Type"]) then
    clear_header("Content-Length")
  end
end

return _M
