#include "tile/net/http/http_response.h"

#include "tile/base/enum.h"
#include "tile/base/internal/early_init.h"
#include "tile/base/likely.h"
#include "tile/net/http/types.h"

namespace tile {
namespace {
static std::array<Slice, 600>
GenStatusCodeDescArray()
{
    std::array<Slice, 600> descs;
    descs[100] = "100 Continue";
    descs[101] = "101 Switching Protocols";
    descs[103] = "103 Early Hints";
    descs[200] = "200 OK";
    descs[201] = "201 Created";
    descs[202] = "202 Accepted";
    descs[203] = "203 Non-Authoritative Information";
    descs[204] = "204 No Content";
    descs[205] = "205 Reset Content";
    descs[206] = "206 Partial Content";
    descs[300] = "300 Multiple Choices";
    descs[301] = "301 Moved Permanently";
    descs[302] = "302 Found";
    descs[303] = "303 See Other";
    descs[304] = "304 Not Modified";
    descs[307] = "307 Temporary Redirect";
    descs[308] = "308 Permanent Redirect";
    descs[400] = "400 Bad Request";
    descs[401] = "401 Unauthorized";
    descs[402] = "402 Payment Required";
    descs[403] = "403 Forbidden";
    descs[404] = "404 Not Found";
    descs[405] = "405 Method Not Allowed";
    descs[406] = "406 Not Acceptable";
    descs[407] = "407 Proxy Authentication Required";
    descs[408] = "408 Request Timeout";
    descs[409] = "409 Conflict";
    descs[410] = "410 Gone";
    descs[411] = "411 Length Required";
    descs[412] = "412 Precondition Failed";
    descs[413] = "413 Payload Too Large";
    descs[414] = "414 URI Too Long";
    descs[415] = "415 Unsupported Media Type";
    descs[416] = "416 Range Not Satisfiable";
    descs[417] = "417 Expectation Failed";
    descs[418] = "418 I'm a teapot";
    descs[422] = "422 Unprocessable Entity";
    descs[425] = "425 Too Early";
    descs[426] = "426 Upgrade Required";
    descs[428] = "428 Precondition Required";
    descs[429] = "429 Too Many Requests";
    descs[431] = "431 Request Header Fields Too Large";
    descs[451] = "451 Unavailable For Legal Reasons";
    descs[500] = "500 Internal Server Error";
    descs[501] = "501 Not Implemented";
    descs[502] = "502 Bad Gateway";
    descs[503] = "503 Service Unavailable";
    descs[504] = "504 Gateway Timeout";
    descs[505] = "505 HTTP Version Not Supported";
    descs[506] = "506 Variant Also Negotiates";
    descs[507] = "507 Insufficient Storage";
    descs[508] = "508 Loop Detected";
    descs[510] = "510 Not Extended";
    descs[511] = "511 Network Authentication Required";
    return descs;
}

static std::array<Slice, 600> *
GetStatusCodeWithDescArray()
{
    static std::array<Slice, 600> kDescArray = GenStatusCodeDescArray();
    return &kDescArray;
}

}// namespace

void
HttpResponse::clear() noexcept
{
    HttpMessage::clear();
    status_ = HttpStatus::OK;
}

std::string
HttpResponse::ToString() const
{
    std::string result;

    result += Format("{} {} {}\r\n", ToSlice(version()), underlying_value(status_), ToSlice(status_));
    for (auto header : *headers()) {
        auto &&key   = header.first;
        auto &&value = header.second;
        result += Format("{}: {}\r\n", key, value);
    }

    result += "\r\n";
    if (auto ptr = noncontiguous_body()) {
        result += FlattenSlow(*ptr);
    } else {
        result += *body();
    }

    return result;
}

void
GenerateDefaultResponsePage(HttpStatus status, HttpResponse *response, Slice title, Slice body)
{
    response->set_status(status);
    response->headers()->Append("Content-Type", "text/html");
    std::string html_body;
    html_body.assign("<html>\n");

    // add title
    {
        html_body.append("<head>\n<title>");
        if (!title.empty()) {
            html_body.append(title);
        } else {
            html_body.append(Format("HTTP {}", http::GetStatusCodeWithDescString(status)));
        }

        html_body.append("</title>\n</head>\n");
    }

    // add body
    {

        html_body.append("<body>");
        if (!body.empty()) {
            html_body.append(body);
        } else {
            html_body.append(Format("{}", http::GetStatusCodeWithDescString(status)));
        }
        html_body.append("</body>\n");
    }
    html_body.append("</html>\n");

    response->set_body(html_body);
}

namespace http {
Slice
GetStatusCodeWithDescString(HttpStatus status) noexcept
{
    auto v = underlying_value(status);
    if (TILE_UNLIKELY(v < 0 || v > GetStatusCodeWithDescArray()->size())) {
        return internal::EarlyInitConstant<Slice>();
    }

    return (*GetStatusCodeWithDescArray())[v];
}

}// namespace http

}// namespace tile
