#include "reply.hpp"
#include <string>

#include "mime_types.hpp"


namespace http::server {
    namespace status_strings {
        const std::string ok = "HTTP/1.1 200 OK\r\n";
        const std::string created = "HTTP/1.1 201 Created\r\n";
        const std::string accepted = "HTTP/1.1 202 Accepted\r\n";
        const std::string no_content = "HTTP/1.1 204 No Content\r\n";
        const std::string multiple_choices = "HTTP/1.1 300 Multiple Choices\r\n";
        const std::string moved_permanently = "HTTP/1.1 301 Moved Permanently\r\n";
        const std::string moved_temporarily = "HTTP/1.1 302 Moved Temporarily\r\n";
        const std::string not_modified = "HTTP/1.1 304 Not Modified\r\n";
        const std::string bad_request = "HTTP/1.1 400 Bad Request\r\n";
        const std::string unauthorized = "HTTP/1.1 401 Unauthorized\r\n";
        const std::string forbidden = "HTTP/1.1 403 Forbidden\r\n";
        const std::string not_found = "HTTP/1.1 404 Not Found\r\n";
        const std::string internal_server_error = "HTTP/1.1 500 Internal Server Error\r\n";
        const std::string not_implemented = "HTTP/1.1 501 Not Implemented\r\n";
        const std::string bad_gateway = "HTTP/1.1 502 Bad Gateway\r\n";
        const std::string service_unavailable = "HTTP/1.1 503 Service Unavailable\r\n";

        boost::asio::const_buffer to_buffer(status_type status) {
            switch (status) {
                case status_type::ok:
                    return boost::asio::buffer(ok);
                case status_type::created:
                    return boost::asio::buffer(created);
                case status_type::accepted:
                    return boost::asio::buffer(accepted);
                case status_type::no_content:
                    return boost::asio::buffer(no_content);
                case status_type::multiple_choices:
                    return boost::asio::buffer(multiple_choices);
                case status_type::moved_permanently:
                    return boost::asio::buffer(moved_permanently);
                case status_type::moved_temporarily:
                    return boost::asio::buffer(moved_temporarily);
                case status_type::not_modified:
                    return boost::asio::buffer(not_modified);
                case status_type::bad_request:
                    return boost::asio::buffer(bad_request);
                case status_type::unauthorized:
                    return boost::asio::buffer(unauthorized);
                case status_type::forbidden:
                    return boost::asio::buffer(forbidden);
                case status_type::not_found:
                    return boost::asio::buffer(not_found);
                case status_type::internal_server_error:
                    return boost::asio::buffer(internal_server_error);
                case status_type::not_implemented:
                    return boost::asio::buffer(not_implemented);
                case status_type::bad_gateway:
                    return boost::asio::buffer(bad_gateway);
                case status_type::service_unavailable:
                    return boost::asio::buffer(service_unavailable);
                default:
                    return boost::asio::buffer(internal_server_error);
            }
        }
    } // namespace status_strings

    namespace misc_strings {
        const char name_value_separator[] = {':', ' '};
        const char crlf[] = {'\r', '\n'};
    } // namespace misc_strings

    std::vector<boost::asio::const_buffer> reply::to_buffers() const {
        std::vector<boost::asio::const_buffer> buffers;
        buffers.push_back(status_strings::to_buffer(status));
        for (const auto & h : headers) {
            buffers.emplace_back(boost::asio::buffer(h.name));
            buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
            buffers.emplace_back(boost::asio::buffer(h.value));
            buffers.push_back(boost::asio::buffer(misc_strings::crlf));
        }
        buffers.emplace_back(boost::asio::buffer(misc_strings::crlf));
        buffers.emplace_back(boost::asio::buffer(content));
        return buffers;
    }

    namespace stock_replies {
        constexpr char ok[] = "";
        constexpr char created[] =
                "<html>"
                "<head><title>Created</title></head>"
                "<body><h1>201 Created</h1></body>"
                "</html>";
        constexpr char accepted[] =
                "<html>"
                "<head><title>Accepted</title></head>"
                "<body><h1>202 Accepted</h1></body>"
                "</html>";
        constexpr char no_content[] =
                "<html>"
                "<head><title>No Content</title></head>"
                "<body><h1>204 Content</h1></body>"
                "</html>";
        constexpr char multiple_choices[] =
                "<html>"
                "<head><title>Multiple Choices</title></head>"
                "<body><h1>300 Multiple Choices</h1></body>"
                "</html>";
        constexpr char moved_permanently[] =
                "<html>"
                "<head><title>Moved Permanently</title></head>"
                "<body><h1>301 Moved Permanently</h1></body>"
                "</html>";
        constexpr char moved_temporarily[] =
                "<html>"
                "<head><title>Moved Temporarily</title></head>"
                "<body><h1>302 Moved Temporarily</h1></body>"
                "</html>";
        constexpr char not_modified[] =
                "<html>"
                "<head><title>Not Modified</title></head>"
                "<body><h1>304 Not Modified</h1></body>"
                "</html>";
        constexpr char bad_request[] =
                "<html>"
                "<head><title>Bad Request</title></head>"
                "<body><h1>400 Bad Request</h1></body>"
                "</html>";
        constexpr char unauthorized[] =
                "<html>"
                "<head><title>Unauthorized</title></head>"
                "<body><h1>401 Unauthorized</h1></body>"
                "</html>";
        constexpr char forbidden[] =
                "<html>"
                "<head><title>Forbidden</title></head>"
                "<body><h1>403 Forbidden</h1></body>"
                "</html>";
        constexpr char not_found[] =
                "<html>"
                "<head><title>Not Found</title></head>"
                "<body><h1>404 Not Found</h1></body>"
                "</html>";
        constexpr char internal_server_error[] =
                "<html>"
                "<head><title>Internal Server Error</title></head>"
                "<body><h1>500 Internal Server Error</h1></body>"
                "</html>";
        constexpr char not_implemented[] =
                "<html>"
                "<head><title>Not Implemented</title></head>"
                "<body><h1>501 Not Implemented</h1></body>"
                "</html>";
        constexpr char bad_gateway[] =
                "<html>"
                "<head><title>Bad Gateway</title></head>"
                "<body><h1>502 Bad Gateway</h1></body>"
                "</html>";
        constexpr char service_unavailable[] =
                "<html>"
                "<head><title>Service Unavailable</title></head>"
                "<body><h1>503 Service Unavailable</h1></body>"
                "</html>";

        std::vector<char> as_content(status_type status) {
            switch (status) {
                case status_type::ok: return {ok, ok + (sizeof(ok) - 1)};
                case status_type::created: return {created, created + (sizeof(created) - 1)};
                case status_type::accepted: return {accepted, accepted + (sizeof(accepted) - 1)};
                case status_type::no_content: return {no_content, no_content + (sizeof(no_content) - 1)};
                case status_type::multiple_choices: return {multiple_choices, multiple_choices + (sizeof(multiple_choices) - 1)};
                case status_type::moved_permanently: return {moved_permanently, moved_permanently + (sizeof(moved_permanently) - 1)};
                case status_type::moved_temporarily: return {moved_temporarily, moved_temporarily + (sizeof(moved_temporarily) - 1)};
                case status_type::not_modified: return {not_modified, not_modified + (sizeof(not_modified) - 1)};
                case status_type::bad_request: return {bad_request, bad_request + (sizeof(bad_request) - 1)};
                case status_type::unauthorized: return {unauthorized, unauthorized + (sizeof(unauthorized) - 1)};
                case status_type::forbidden: return {forbidden, forbidden + (sizeof(forbidden) - 1)};
                case status_type::not_found: return {not_found, not_found + (sizeof(not_found) - 1)};
                case status_type::not_implemented: return {not_implemented, not_implemented + (sizeof(not_implemented) - 1)};
                case status_type::bad_gateway: return {bad_gateway, bad_gateway + (sizeof(bad_gateway) - 1)};
                case status_type::service_unavailable: return {service_unavailable, service_unavailable + (sizeof(service_unavailable) - 1)};
                case status_type::internal_server_error:
                default: return {internal_server_error, internal_server_error + (sizeof(internal_server_error) - 1)};
            }
        }

        void as_content(status_type status, std::vector<char>& dest) {
            dest.clear();
            switch (status) {
                case status_type::ok: dest.insert(dest.end(), ok, ok + sizeof(ok) - 1);  return;
                case status_type::created: dest.insert(dest.end(), created, created + sizeof(created) - 1); return;
                case status_type::accepted: dest.insert(dest.end(), accepted, accepted + sizeof(accepted) - 1); return;
                case status_type::no_content: dest.insert(dest.end(), no_content, no_content + sizeof(no_content) - 1); return;
                case status_type::multiple_choices: dest.insert(dest.end(), multiple_choices, multiple_choices + sizeof(multiple_choices) - 1); return;
                case status_type::moved_permanently: dest.insert(dest.end(), moved_permanently, moved_permanently + sizeof(moved_permanently) - 1); return;
                case status_type::moved_temporarily: dest.insert(dest.end(), moved_temporarily, moved_temporarily + sizeof(moved_temporarily) - 1); return;
                case status_type::not_modified: dest.insert(dest.end(), not_modified, not_modified + sizeof(not_modified) - 1); return;
                case status_type::bad_request: dest.insert(dest.end(), bad_request, bad_request + sizeof(bad_request) - 1); return;
                case status_type::unauthorized: dest.insert(dest.end(), unauthorized, unauthorized + sizeof(unauthorized) - 1); return;
                case status_type::forbidden: dest.insert(dest.end(), forbidden, forbidden + sizeof(forbidden) - 1); return;
                case status_type::not_found: dest.insert(dest.end(), not_found, not_found + sizeof(not_found) - 1); return;
                case status_type::not_implemented: dest.insert(dest.end(), not_implemented, not_implemented + sizeof(not_implemented) - 1); return;
                case status_type::bad_gateway: dest.insert(dest.end(), bad_gateway, bad_gateway + sizeof(bad_gateway) - 1); return;
                case status_type::service_unavailable: dest.insert(dest.end(), service_unavailable, service_unavailable + sizeof(service_unavailable) - 1); return;
                case status_type::internal_server_error:
                default: dest.insert(dest.end(), internal_server_error, internal_server_error + sizeof(internal_server_error) - 1);
            }
        }
    } // namespace stock_replies

    void stockReply(status_type status, reply& rep) {
        rep.status = status;
        rep.headers.clear();
        rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_html)});
        stock_replies::as_content(status, rep.content);
    }
} // namespace http::server
