#pragma once

#include "dps/config.hpp"

#include <functional>
#include <map>
#include <optional>
#include <string>
#include <vector>

namespace dps {

struct Request {
    std::string method;
    std::string raw_target;
    std::string path;
    std::string body;
    std::string remote_addr;
    std::map<std::string, std::string> headers;
    std::map<std::string, std::string> query;
    std::map<std::string, std::string> path_params;
    std::optional<std::string> current_user;

    std::string header(const std::string &name) const;
    std::string query_value(const std::string &name) const;
    std::string path_param(const std::string &name) const;
};

struct Response {
    int status = 200;
    std::string content_type = "application/json; charset=utf-8";
    std::string body;
    std::vector<unsigned char> binary_body;
    std::map<std::string, std::string> headers;

    bool has_binary() const;
};

using Handler = std::function<Response(const Request &)>;

class Router {
public:
    void add(const std::string &method, const std::string &pattern, bool requires_auth, Handler handler);
    Response dispatch(Request request) const;

private:
    struct Route {
        std::string method;
        std::string pattern;
        bool requires_auth = false;
        Handler handler;
    };

    std::vector<Route> routes_;
};

class HttpServer {
public:
    HttpServer(std::string host, int port, LoggingConfig logging, const Router &router);
    int run() const;

private:
    std::string host_;
    int port_;
    LoggingConfig logging_;
    const Router &router_;
};

std::string url_decode(const std::string &value);
std::map<std::string, std::string> parse_query_string(const std::string &query);
std::string guess_content_type(const std::string &path);

}  // namespace dps
