local kong_storage = require "kong.plugins.session.storage.kong"
local resty_session = require "resty.session"


local kong = kong
local ipairs = ipairs


-- In theory bodies are allowed in most HTTP methods, but in
-- practice it is reasonable to limit reading bodies only to
-- below list of HTTP methods.
local READ_BODY_METHODS = {
  DELETE = true, -- this is a stretch, but lets allow it
  PATCH = true,
  POST = true,
  PUT = true,
}


local _M = {}


--- Open a session based on plugin config
-- @returns resty.session session object
function _M.open_session(conf)
  return resty_session.open({
    secret                    = conf.secret,
    audience                  = conf.audience,
    storage                   = conf.storage == "kong" and kong_storage,
    idling_timeout            = conf.idling_timeout,
    rolling_timeout           = conf.rolling_timeout,
    absolute_timeout          = conf.absolute_timeout,
    stale_ttl                 = conf.stale_ttl,
    cookie_name               = conf.cookie_name,
    cookie_path               = conf.cookie_path,
    cookie_domain             = conf.cookie_domain,
    cookie_same_site          = conf.cookie_same_site,
    cookie_http_only          = conf.cookie_http_only,
    cookie_secure             = conf.cookie_secure,
    remember                  = conf.remember,
    remember_cookie_name      = conf.remember_cookie_name,
    remember_rolling_timeout  = conf.remember_rolling_timeout,
    remember_absolute_timeout = conf.remember_absolute_timeout,
    response_headers          = conf.response_headers,
    request_headers           = conf.request_headers,
    hash_subject              = conf.hash_subject,
    store_metadata            = conf.store_metadata,
  })
end


--- Gets consumer id and credential id from the session data
-- @param session - the session
-- @returns consumer_id, credential_id, groups
function _M.get_session_data(session)
  if not session then
    return
  end

  local data = session:get_data()
  if not data then
    return
  end

  return data[1], data[2], data[3]
end


--- Store the session data for usage in kong plugins
-- @param session - the session
-- @param consumer_id - the consumer id
-- @param credential_id - the credential id or potentially just the consumer id
-- @param groups - table of authenticated_groups e.g. { "group1" }
function _M.set_session_data(session, consumer_id, credential_id, groups)
  if not session then
    return
  end

  session:set_data({
    consumer_id,
    credential_id,
    groups,
  })
end


--- Determine is incoming request is trying to logout
-- @return boolean should logout of the session?
function _M.logout(conf)
  local logout_methods = conf.logout_methods
  if not logout_methods then
    return false
  end

  local request_method = kong.request.get_method()
  local logout
  for _, logout_method in ipairs(logout_methods) do
    if logout_method == request_method then
      logout = true
      break
    end
  end

  if not logout then
    return false
  end

  local logout_query_arg = conf.logout_query_arg
  if logout_query_arg then
    if kong.request.get_query_arg(logout_query_arg) then
      kong.log.debug("logout by query argument")
      return true
    end
  end

  -- If the request method is POST or DELETE, then check the body for the logout post args
  if conf.read_body_for_logout then
    local logout_post_arg = conf.logout_post_arg
    if logout_post_arg and READ_BODY_METHODS[request_method] then
      local post_args = kong.request.get_body()
      if post_args and post_args[logout_post_arg] then
        kong.log.debug("logout by post argument")
        return true
      end
    end
  end
  return false
end


return _M
