local pl_path = require "pl.path"
local splitn = require("kong.tools.string").splitn

local env_config = {
  debugger = os.getenv("KONG_EMMY_DEBUGGER"),
  host = os.getenv("KONG_EMMY_DEBUGGER_HOST"),
  port = os.getenv("KONG_EMMY_DEBUGGER_PORT"),
  wait = os.getenv("KONG_EMMY_DEBUGGER_WAIT"),
  source_path = os.getenv("KONG_EMMY_DEBUGGER_SOURCE_PATH"),
  multi_worker = os.getenv("KONG_EMMY_DEBUGGER_MULTI_WORKER"),
}

local source_path
local env_prefix

local function find_source(path)
  if pl_path.exists(path) then
    return path
  end

  if path:match("^=") then
    -- code is executing from .conf file, don't attempt to map
    return path
  end

  if path:match("^jsonschema:") then
    -- code is executing from jsonschema, don't attempt to map
    return path
  end

  for _, p in ipairs(source_path) do
    local full_path = pl_path.join(p, path)
    if pl_path.exists(full_path) then
      return full_path
    end
  end

  ngx.log(ngx.ERR, "source file ", path, " not found in ", env_prefix, "_EMMY_DEBUGGER_SOURCE_PATH")

  return path
end

local function load_debugger(path)
  _G.emmy = {
    fixPath = find_source
  }

  local ext = pl_path.extension(path)
  local name = pl_path.basename(path):sub(1, -#ext - 1)

  local save_cpath = package.cpath
  package.cpath = pl_path.dirname(path) .. '/?' .. ext
  local dbg = require(name)
  package.cpath = save_cpath
  return dbg
end

local function init(config_)
  local config = config_ or {}
  local debugger = config.debugger or env_config.debugger
  local host = config.host or env_config.host or "localhost"
  local port = config.port or env_config.port or 9966
  local wait = config.wait or env_config.wait
  local multi_worker = env_config.multi_worker or env_config.multi_worker

  env_prefix = config.env_prefix or "KONG"
  source_path = splitn(config.source_path or env_config.source_path or "", ":")

  if not debugger then
    return
  end

  if not pl_path.isabs(debugger) then
    ngx.log(ngx.ERR, env_prefix, "_EMMY_DEBUGGER (", debugger, ") must be an absolute path")
    return
  end
  if not pl_path.exists(debugger) then
    ngx.log(ngx.ERR, env_prefix, "_EMMY_DEBUGGER (", debugger, ") file not found")
    return
  end
  local ext = pl_path.extension(debugger)
  if ext ~= ".so" and ext ~= ".dylib" then
    ngx.log(ngx.ERR, env_prefix, "_EMMY_DEBUGGER (", debugger, ") must be a .so (Linux) or .dylib (macOS) file")
    return
  end
  if ngx.worker.id() > 0 and not multi_worker then
    ngx.log(ngx.ERR, env_prefix, "_EMMY_DEBUGGER is only supported in the first worker process, suggest setting KONG_NGINX_WORKER_PROCESSES to 1")
    return
  end

  ngx.log(ngx.NOTICE, "loading EmmyLua debugger ", debugger)
  ngx.log(ngx.WARN, "The EmmyLua integration for Kong is a feature solely for your convenience during development. Kong assumes no liability as a result of using the integration and does not endorse it’s usage. Issues related to usage of EmmyLua integration should be directed to the respective project instead.")

  local dbg = load_debugger(debugger)
  dbg.tcpListen(host, port + (ngx.worker.id() or 0))

  ngx.log(ngx.NOTICE, "EmmyLua debugger loaded, listening on port ", port)

  if wait then
    -- Wait for IDE connection
    ngx.log(ngx.NOTICE, "waiting for IDE to connect")
    dbg.waitIDE()
    ngx.log(ngx.NOTICE, "IDE connected")
  end
end

return {
  init = init,
  load_debugger = load_debugger
}
