#pragma once

#include "app.h"

#include <functional>
#include <httppp/http.h>
#include <httppp/httpd.h>
#include <httppp/hpack.h>
#include <httppp/qpack.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;
};

// Per-connection incremental import state
struct ImportState {
    std::unique_ptr<ImportSession> session;
    size_t content_length = 0;
    size_t bytes_written = 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;

  protected:
    void RequestEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;

    // Streaming body callbacks for H2/H3 import
    bool onH2StreamHeaders(libhttppp::HttpRequest &conn, uint32_t streamId,
                           const std::vector<libhttppp::hpack::HeaderField> &headers) override;
    bool onH3StreamHeaders(netplus::socket *sock, uint64_t streamId,
                           const std::vector<libhttppp::qpack::HeaderField> &headers) override;
    void onH2DataChunk(libhttppp::HttpRequest &conn, uint32_t streamId,
                       const char *data, size_t len, bool endStream,
                       std::string &h2out, const int tid, ULONG_PTR args) override;
    void onH3DataChunk(netplus::socket *sock, uint64_t streamId,
                       const char *data, size_t len, bool fin) override;

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

    // Per-connection import sessions: keyed by con pointer (H1)
    std::unordered_map<netplus::con*, ImportState> active_imports_;

    // Per-stream import sessions for H2 (keyed by HttpRequest* + streamId)
    struct H2StreamKey {
        libhttppp::HttpRequest* conn;
        uint32_t streamId;
        bool operator==(const H2StreamKey &o) const { return conn == o.conn && streamId == o.streamId; }
    };
    struct H2StreamKeyHash {
        size_t operator()(const H2StreamKey &k) const {
            return std::hash<void*>()(k.conn) ^ (std::hash<uint32_t>()(k.streamId) << 16);
        }
    };
    std::unordered_map<H2StreamKey, ImportState, H2StreamKeyHash> h2_imports_;

    // Per-stream import sessions for H3 (keyed by streamId)
    std::unordered_map<uint64_t, ImportState> h3_imports_;

    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,
               const std::string& db_dir = "");
    int run();

    // Called after fork (daemon) or immediately (foreground), before event loop
    std::function<void()> post_fork_callback;

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

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

} // namespace mediadb
