local helpers = require "spec.helpers"
local http = require "resty.http"

local dp_status_port = 8100

local function get_status_no_ssl_verify()
  local httpc = http.new()

  local ok, err = httpc:connect({
      scheme = "https",
      host = "127.0.0.1",
      port = dp_status_port,
      ssl_verify = false,
  })
  if not ok then
      return nil, err
  end

  local res, err = httpc:request({
      path = "/status/ready",
      headers = {
          ["Content-Type"] = "application/json",
      }
  })

  if not res then
    return nil, err
  end

  return res.status
end

local function verify_status(port, code)
  local status_client =  assert(helpers.http_client("127.0.0.1", port, 20000))

  local res = status_client:send({
    method = "GET",
    path = "/status/ready",
  })

  status_client:close()
  local status = res and res.status

  if status == code then
    return true
  end

  return false
end

for _, strategy in helpers.each_strategy() do
  if strategy ~= "off" then
    -- skip the "off" strategy, as dbless has its own test suite
    describe("kong drain with #" .. strategy .. " backend", function()
      lazy_setup(function()
        helpers.get_db_utils(strategy, {}) -- runs migrations

        assert(helpers.start_kong({
          database = strategy,
          nginx_conf = "spec/fixtures/custom_nginx.template",
          status_listen = "127.0.0.1:8100",
          nginx_main_worker_processes = 8,
          log_level = "info",
        }))
      end)

      lazy_teardown(function()
        helpers.stop_kong()
      end)

      it("should set Kong to 'draining'", function()
        helpers.wait_until(function()
          return verify_status(dp_status_port, 200)
        end, 10)

        local ok, err, msg = helpers.kong_exec("drain", {
          prefix = helpers.test_conf.prefix,
        })
        assert.equal("", err)
        assert.equal("Kong's status successfully changed to 'draining'\n", msg)
        assert.equal(true, ok)


        helpers.wait_until(function()
          return verify_status(dp_status_port, 503)
        end, 10)
      end)
    end)

    describe("Kong without a status listener", function()
      lazy_setup(function()
        helpers.get_db_utils(strategy, {}) -- runs migrations

        assert(helpers.start_kong({
          database = strategy,
          nginx_conf = "spec/fixtures/custom_nginx.template",
        }))
      end)

      lazy_teardown(function()
        helpers.stop_kong()
      end)

      it("should return an error when trying to set 'draining' without a status listener", function()
        local ok, err, msg = helpers.kong_exec("drain", {
          prefix = helpers.test_conf.prefix,
        })
        assert.equal("", err)
        assert.equal("No status listeners found in configuration.\n", msg)
        assert.equal(true, ok)
      end)

    end)

    describe("Kong with SSL-enabled status listener", function()
      lazy_setup(function()
        helpers.get_db_utils(strategy, {}) -- runs migrations

        assert(helpers.start_kong({
          database = strategy,
          nginx_conf = "spec/fixtures/custom_nginx.template",
          status_listen = "127.0.0.1:8100 ssl",
          nginx_main_worker_processes = 8,
        }))
      end)

      lazy_teardown(function()
        helpers.stop_kong()
      end)

      it("should set Kong to 'draining' with SSL-enabled status listener", function()
        helpers.wait_until(function()
          local status = get_status_no_ssl_verify()
          if status == 200 then
            return true
          end
        end, 10)

        local ok, err, msg = helpers.kong_exec("drain", {
          prefix = helpers.test_conf.prefix,
        })
        assert.equal("", err)
        assert.equal("Kong's status successfully changed to 'draining'\n", msg)
        assert.equal(true, ok)

        helpers.wait_until(function()
          local status = get_status_no_ssl_verify()
          if status == 503 then
            return true
          end
        end, 10)
      end)
    end)
  end
end

for _, strategy in helpers.each_strategy({"postgres"}) do
  describe("kong drain in hybrid mode #" .. strategy, function()
    local cp_status_port

    lazy_setup(function()
      cp_status_port = helpers.get_available_port()

      local bp = helpers.get_db_utils(strategy, {
        "services",
      })

      -- insert some data to make sure the control plane is ready and send the configuration to dp
      -- so that `current_hash` of dp wouldn't be DECLARATIVE_EMPTY_CONFIG_HASH, so that dp would be ready
      assert(bp.services:insert {
        name = "example",
      })

      assert(helpers.start_kong({
        role = "data_plane",
        database = "off",
        cluster_cert = "spec/fixtures/kong_clustering.crt",
        cluster_cert_key = "spec/fixtures/kong_clustering.key",
        cluster_control_plane = "127.0.0.1:9005",
        proxy_listen = "127.0.0.1:9002",
        nginx_worker_processes = 8,
        status_listen = "127.0.0.1:" .. dp_status_port,
        prefix = "serve_dp",
        log_level = "info",
      }))

      assert(helpers.start_kong({
        role = "control_plane",
        cluster_cert = "spec/fixtures/kong_clustering.crt",
        cluster_cert_key = "spec/fixtures/kong_clustering.key",
        database = strategy,
        prefix = "serve_cp",
        cluster_listen = "127.0.0.1:9005",
        nginx_conf = "spec/fixtures/custom_nginx.template",
        status_listen = "127.0.0.1:" .. cp_status_port
      }))
    end)

    lazy_teardown(function()
      helpers.stop_kong("serve_dp")
      helpers.stop_kong("serve_cp")
    end)

    it("should set Kong to 'draining'", function()
      helpers.wait_until(function()
        return verify_status(dp_status_port, 200)
      end, 10)

      -- set dp to draining
      local ok, err, msg = helpers.kong_exec("drain --prefix serve_dp", {
        prefix = helpers.test_conf.prefix,
        database = "off",
      })
      assert.equal("", err)
      assert.equal("Kong's status successfully changed to 'draining'\n", msg)
      assert.equal(true, ok)

      helpers.wait_until(function()
        return verify_status(dp_status_port, 503)
      end, 10)

      -- set cp to draining
      local ok, err, msg = helpers.kong_exec("drain --prefix serve_cp", {
        prefix = helpers.test_conf.prefix,
      })
      assert.equal("", err)
      assert.equal("Kong's status successfully changed to 'draining'\n", msg)
      assert.equal(true, ok)

      helpers.wait_until(function()
        return verify_status(cp_status_port, 503)
      end, 10)
    end)
  end)
end

describe("kong drain in DB-less mode #off", function()
  local admin_client

  lazy_setup(function()
    assert(helpers.start_kong ({
      status_listen = "127.0.0.1:8100",
      plugins = "admin-api-method",
      database = "off",
      nginx_main_worker_processes = 8,
      log_level = "info",
    }))
  end)

  before_each(function()
    admin_client = helpers.admin_client()
  end)

  after_each(function()
    if admin_client then
      admin_client:close()
    end
  end)

  lazy_teardown(function()
    helpers.stop_kong()
  end)

  it("should set Kong to 'draining'", function()
    local res = assert(admin_client:send {
      method = "POST",
      path = "/config",
      body = {
        config = [[
        _format_version: "3.0"
        services:
          - name: example
            url: http://mockbin.org
        ]]
      },
      headers = {
        ["Content-Type"] = "multipart/form-data"
      },
    })

    assert.res_status(201, res)

    helpers.wait_until(function()
      return verify_status(dp_status_port, 200)
    end, 10)


    local ok, err, msg = helpers.kong_exec("drain", {
      prefix = helpers.test_conf.prefix,
      database = "off",
    })
    assert.equal("", err)
    assert.equal("Kong's status successfully changed to 'draining'\n", msg)
    assert.equal(true, ok)

    helpers.wait_until(function()
      return verify_status(dp_status_port, 503)
    end, 10)
  end)
end)
