#pragma once

#include <string>
#include <vector>
#include <memory>
#include <mutex>
#include <nlohmann/json.hpp>

#include <drogon/drogon.h>
#include "database.hpp"
#include "config.hpp"

namespace simple_object_storage {

class PutHandler;  // Forward declaration
class UpdateHandler; // Forward declaration
class RateLimiter; // Forward declaration

class Server {
public:
    Server(const ServerConfig& config);
    ~Server();
    
    static Server* getInstance() {
        std::lock_guard<std::mutex> lock(instance_mutex_);
        return instance_;
    }
    static void setInstance(Server* server) {
        std::lock_guard<std::mutex> lock(instance_mutex_);
        instance_ = server;
    }

    bool start();
    void stop();
    bool validate_write_request(const drogon::HttpRequestPtr& req, drogon::HttpResponsePtr& res, const std::vector<std::string>& required_params, std::map<std::string, std::string>& params);
    std::pair<std::string, std::string> parse_labeltag(const std::string& labeltag) const;

    // Make these public so PutHandler can access them
    ServerConfig config_;
    std::unique_ptr<Database> db_;
    std::unique_ptr<PutHandler> put_handler_;
    std::unique_ptr<UpdateHandler> update_handler_;

    void handle_get_version(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback);
    void handle_get_object(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback, const std::string& key);
    void handle_get_hash(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback, const std::string& key);
    void handle_get_directory(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback);
    void handle_get_metadata(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback, const std::string& key);
    void handle_delete_object(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback);
    void handle_delete_old_objects(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback);
    void handle_exists(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback, const std::string& key);
    void handle_cors_preflight(const drogon::HttpRequestPtr& req, std::function<void(const drogon::HttpResponsePtr &)>&& callback);
    void add_cors_headers(const drogon::HttpRequestPtr& req, const drogon::HttpResponsePtr& res);
    void add_security_headers(const drogon::HttpResponsePtr& res);

private:
    void setup_routes();
    std::string join(const std::vector<std::string>& strings, const std::string& delimiter);

    bool init_db();

    bool running_;
    std::unique_ptr<RateLimiter> auth_rate_limiter_;
    
    static Server* instance_;
    static std::mutex instance_mutex_;
};

} // namespace simple_object_storage 