// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#define CAF_SUITE net.http.server

#include "caf/net/http/server.hpp"

#include "net-test.hpp"

using namespace caf;
using namespace caf::literals;
using namespace std::literals::string_literals;

namespace {

struct app_t {
  net::http::header hdr;

  caf::byte_buffer payload;

  template <class LowerLayerPtr>
  error init(net::socket_manager*, LowerLayerPtr, const settings&) {
    return none;
  }

  template <class LowerLayerPtr>
  bool prepare_send(LowerLayerPtr) {
    return true;
  }

  template <class LowerLayerPtr>
  bool done_sending(LowerLayerPtr) {
    return true;
  }

  template <class LowerLayerPtr>
  void abort(LowerLayerPtr, const error& reason) {
    CAF_FAIL("app::abort called: " << reason);
  }

  template <class LowerLayerPtr>
  bool consume(LowerLayerPtr down, net::http::context ctx,
               const net::http::header& request_hdr,
               caf::const_byte_span body) {
    hdr = request_hdr;
    down->send_response(ctx, net::http::status::ok, "text/plain",
                        "Hello world!");
    payload.assign(body.begin(), body.end());
    return true;
  }

  string_view field(string_view key) {
    if (auto i = hdr.fields().find(key); i != hdr.fields().end())
      return i->second;
    else
      return {};
  }

  string_view param(string_view key) {
    auto& qm = hdr.query();
    if (auto i = qm.find(key); i != qm.end())
      return i->second;
    else
      return {};
  }
};

using mock_server_type = mock_stream_transport<net::http::server<app_t>>;

} // namespace

BEGIN_FIXTURE_SCOPE(host_fixture)

SCENARIO("the server parses HTTP GET requests into header fields") {
  GIVEN("valid HTTP GET request") {
    string_view req = "GET /foo/bar?user=foo&pw=bar HTTP/1.1\r\n"
                      "Host: localhost:8090\r\n"
                      "User-Agent: AwesomeLib/1.0\r\n"
                      "Accept-Encoding: gzip\r\n\r\n";
    string_view res = "HTTP/1.1 200 OK\r\n"
                      "Content-Type: text/plain\r\n"
                      "Content-Length: 12\r\n"
                      "\r\n"
                      "Hello world!";
    WHEN("sending it to an HTTP server") {
      mock_server_type serv;
      CHECK_EQ(serv.init(), error{});
      serv.push(req);
      THEN("the HTTP layer parses the data and calls the application layer") {
        CHECK_EQ(serv.handle_input(), static_cast<ptrdiff_t>(req.size()));
        auto& app = serv.upper_layer.upper_layer();
        auto& hdr = app.hdr;
        CHECK_EQ(hdr.method(), net::http::method::get);
        CHECK_EQ(hdr.version(), "HTTP/1.1");
        CHECK_EQ(hdr.path(), "foo/bar");
        CHECK_EQ(app.field("Host"), "localhost:8090");
        CHECK_EQ(app.field("User-Agent"), "AwesomeLib/1.0");
        CHECK_EQ(app.field("Accept-Encoding"), "gzip");
      }
      AND("the server properly formats a response from the application layer") {
        CHECK_EQ(serv.output_as_str(), res);
      }
    }
  }
}

END_FIXTURE_SCOPE()
