#pragma once

#include "app.h"

#include <httppp/http.h>
#include <httppp/httpd.h>
#include <httppp/exception.h>
#include <netplus/exception.h>
#include <netplus/socket.h>

#include <atomic>
#include <string>
#include <unordered_map>
#include <vector>

namespace mediadb {

// Per-connection streaming state for large media responses
struct MediaConExt {
    std::vector<std::uint8_t> data;
    size_t send_size = 0;
    size_t produced = 0;
};

MediaConExt* getMediaExt(libhttppp::HttpRequest& req);
MediaConExt* ensureMediaExt(libhttppp::HttpRequest& req);

class MediaHttpEvent : public libhttppp::HttpEvent {
  public:
    MediaHttpEvent(App& app, std::vector<netplus::socket*> sockets);

    void RequestEvent(libhttppp::HttpRequest& curreq, const int tid, ULONG_PTR args) override;
    void ResponseEvent(libhttppp::HttpRequest& curreq, const int tid, ULONG_PTR args) override;
    void ConnectEvent(libhttppp::HttpRequest& curreq, const int tid, ULONG_PTR args) override;
    void DisconnectEvent(libhttppp::HttpRequest& curreq, const int tid, ULONG_PTR args) override;

  private:
    static std::unordered_map<std::string, std::string> parse_query(const std::string& raw_url);
    static HttpRequest convert_request(libhttppp::HttpRequest& curreq);
    static std::string status_to_http_state(int status);
    static void send_response(libhttppp::HttpRequest& curreq, const HttpResponse& mresp);

    App& app_;
};

class HttpServer {
  public:
    HttpServer(App& app, const std::string& addr, int port, int max_connections,
               const std::string& cert_path, const std::string& key_path,
               bool foreground, const std::string& run_user);
    int run();

  private:
    static void drop_privileges(const std::string& username);

    App& app_;
    std::string addr_;
    int port_;
    int max_connections_;
    std::string cert_path_;
    std::string key_path_;
    bool foreground_;
    std::string run_user_;
};

} // namespace mediadb
