#include <crow/app.h>

#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
#include <gzip_helper.hpp>
#include <sstream>
#include <webassets.hpp>

#include "gtest/gtest.h"
#include <gmock/gmock.h>

using namespace crow;
using namespace std;
using namespace testing;

// Tests static files are loaded correctly
TEST(Webassets, StaticFilesFixedRoutes)
{
    std::array<char, 2048> buf;
    SimpleApp app;
    webassets::requestRoutes(app);
    Server<SimpleApp> server(&app, "127.0.0.1", 45451);
    auto _ = async(launch::async, [&] { server.run(); });

    // get the homepage
    std::string sendmsg = "GET /\r\n\r\n";

    asio::io_context is;

    asio::ip::tcp::socket c(is);
    c.connect(asio::ip::tcp::endpoint(
        asio::ip::address::from_string("127.0.0.1"), 45451));

    c.send(asio::buffer(sendmsg));

    c.receive(asio::buffer(buf, 2048));
    c.close();

    std::string response(std::begin(buf), std::end(buf));
    // This is a routine to split strings until a newline is hit
    // TODO(ed) this should really use the HTTP parser
    std::vector<std::string> headers;
    std::string::size_type pos = 0;
    std::string::size_type prev = 0;
    int content_length = 0;
    std::string content_encoding("");
    while ((pos = response.find("\r\n", prev)) != std::string::npos)
    {
        auto this_string = response.substr(prev, pos - prev);
        if (this_string == "")
        {
            prev = pos + 2;
            break;
        }

        if (boost::starts_with(this_string, "Content-Length: "))
        {
            content_length = boost::lexical_cast<int>(this_string.substr(16));
            // TODO(ed) This is an unfortunate test, but it's all we have at
            // this point Realistically, the index.html will be more than 500
            // bytes.  This test will need to be improved at some point
            EXPECT_GT(content_length, 500);
        }
        if (boost::starts_with(this_string, "Content-Encoding: "))
        {
            content_encoding = this_string.substr(18);
        }

        headers.push_back(this_string);
        prev = pos + 2;
    }

    auto http_content = response.substr(prev);
    // TODO(ed) ideally the server should support non-compressed gzip assets.
    // Once this occurs, this line will be obsolete
    std::string ungziped_content = http_content;
    if (content_encoding == "gzip")
    {
        EXPECT_TRUE(gzipInflate(http_content, ungziped_content));
    }

    EXPECT_EQ(headers[0], "HTTP/1.1 200 OK");
    EXPECT_THAT(headers,
                ::testing::Contains("Content-Type: text/html;charset=UTF-8"));

    EXPECT_EQ(ungziped_content.substr(0, 21), "<!DOCTYPE html>\n<html");

    server.stop();
}

// Tests static files are loaded correctly
TEST(Webassets, EtagIsSane)
{
    std::array<char, 2048> buf;
    SimpleApp app;
    webassets::requestRoutes(app);
    Server<SimpleApp> server(&app, "127.0.0.1", 45451);
    auto _ = async(launch::async, [&] { server.run(); });

    // get the homepage
    std::string sendmsg = "GET /\r\n\r\n";

    asio::io_context is;

    asio::ip::tcp::socket c(is);
    c.connect(asio::ip::tcp::endpoint(
        asio::ip::address::from_string("127.0.0.1"), 45451));

    c.send(asio::buffer(sendmsg));

    c.receive(asio::buffer(buf, 2048));
    c.close();

    std::string response(std::begin(buf), std::end(buf));
    // This is a routine to split strings until a newline is hit
    // TODO(ed) this should really use the HTTP parser
    std::vector<std::string> headers;
    std::string::size_type pos = 0;
    std::string::size_type prev = 0;
    int content_length = 0;
    std::string content_encoding("");
    while ((pos = response.find("\r\n", prev)) != std::string::npos)
    {
        auto this_string = response.substr(prev, pos - prev);
        if (this_string == "")
        {
            break;
        }

        if (boost::starts_with(this_string, "ETag: "))
        {
            auto etag = this_string.substr(6);
            // ETAG should not be blank
            EXPECT_NE(etag, "");
            // SHa1 is 20 characters long
            EXPECT_EQ(etag.size(), 40);
            EXPECT_THAT(etag, MatchesRegex("^[a-f0-9]+$"));
        }

        headers.push_back(this_string);
        prev = pos + 2;
    }

    server.stop();
}
