#include "../test.h"
#include <regex>

#define TEST(name) TEST_CASE("client-connect-request: " name, "[client-connect-request]")

using namespace Catch::Matchers;

TEST("parser create") {
    ClientParser p;
    CHECK(!p.established());
}

TEST("default request") {
    ClientParser p;
    p.no_deflate();
    auto req = make_iptr<ConnectRequest>();
    req->uri = new URI("ws://crazypanda.ru:4321/path?a=b");
    req->ws_protocol("fuck");
    req->ws_extensions({
        {"permessage-deflate", {{"client_max_window_bits" , ""}}},
    });
    req->ws_version(12);
    req->headers = Headers({
        {"Accept-Encoding", "gzip, deflate, sdch"},
        {"Origin",          "http://www.crazypanda.ru"},
        {"Cache-Control",   "no-cache"},
        {"User-Agent",      "PWS-Test"},
    });

    auto str = p.connect_request(req);
    CHECK_THAT(str, StartsWith("GET /path?a=b HTTP/1.1\r\n"));
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Protocol: fuck\r\n"));
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Version: 12\r\n"));
    CHECK_THAT(str, MatchesRe("Sec-WebSocket-Key: (.+)\r\n"));
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"));
    CHECK_THAT(str, ContainsSubstring("Connection: Upgrade\r\n"));
    CHECK_THAT(str, ContainsSubstring("Upgrade: websocket\r\n"));
    CHECK_THAT(str, ContainsSubstring("Origin: http://www.crazypanda.ru\r\n"));
    CHECK_THAT(str, ContainsSubstring("Accept-Encoding: gzip, deflate, sdch\r\n"));
    CHECK_THAT(str, ContainsSubstring("Cache-Control: no-cache\r\n"));
    CHECK_THAT(str, ContainsSubstring("User-Agent: PWS-Test\r\n"));
    CHECK_THAT(str, ContainsSubstring("Host: crazypanda.ru\r\n"));

    CHECK(!p.established());

    CHECK_THROWS_AS(p.connect_request(req), Error);
}

TEST("default values") {
    ClientParser p;
    auto str = p.connect_request(ConnectRequest::Builder().uri("ws://crazypanda.ru:4321/path?a=b").build());
    CHECK_THAT(str, StartsWith("GET /path?a=b HTTP/1.1\r\n"));
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Version: 13\r\n"));
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15; server_max_window_bits=15\r\n"));
    CHECK_THAT(str, !ContainsSubstring("Sec-Websocket-Protocol: \r\n"));
}

TEST("custom ws_key") {
    ClientParser p;
    auto str = p.connect_request(ConnectRequest::Builder()
        .uri(new URI("ws://crazypanda.ru:4321/path?a=b"))
        .ws_key("suka")
        .build()
    );
    CHECK_THAT(str, ContainsSubstring("Sec-WebSocket-Key: suka\r\n"));
}

TEST("empty relative url") {
    ClientParser p;
    auto str = p.connect_request(ConnectRequest::Builder().uri("wss://crazypanda.ru").build());
    CHECK_THAT(str, StartsWith("GET / HTTP/1.1\r\n"));
}

TEST("server parser accepts connect request") {
    ClientParser p;
    ServerParser sp;
    auto str = p.connect_request(ConnectRequest::Builder().uri("ws://crazypanda.ru/path?a=b").build());
    str = string(std::regex_replace((std::string)str, std::regex("websocket"), "WebSocket")); // check case insensitive
    auto req = sp.accept(str);
    CHECK(sp.accepted());
    REQUIRE(req);
    CHECK_FALSE(req->error());
    CHECK(req->ws_version() == 13);
    CHECK(req->uri->to_string() == "/path?a=b");
}

TEST("no host in uri") {
    ClientParser p;
    auto req = ConnectRequest::Builder().uri("ws:path?a=b").build();
    CHECK_THROWS_AS(p.connect_request(req), Error);
}

TEST("wrong scheme") {
    ClientParser p;
    auto req = ConnectRequest::Builder().uri("wsss://dev.ru/").build();
    CHECK_THROWS_AS(p.connect_request(req), Error);
}

TEST("body is not allowed") {
    ClientParser p;
    auto req = ConnectRequest::Builder().uri("ws://dev.ru/").body("hello world").build();
    CHECK_THROWS_AS(p.connect_request(req), Error);
}

TEST("to_string twice") {
    ClientParser p;
    auto req = ConnectRequest::Builder().uri("wss://crazypanda.ru").build();
    auto first = p.connect_request(req);
    p.reset();
    auto second = p.connect_request(req);
    CHECK(first == second);
}