#pragma once

/**
 * @brief Simple HTTP Client wrapper around cpp-httplib
 *
 * Header-only wrapper for making HTTP requests to LLM APIs.
 * Requires cpp-httplib and OpenSSL for HTTPS support.
 */

// Only enable OpenSSL support if it was found during CMake configuration
// CPPHTTPLIB_OPENSSL_SUPPORT is defined via target_compile_definitions when OpenSSL is available
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <string>
#include <optional>
#include <map>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>

namespace celuna {

using json = nlohmann::json;

struct HttpResponse {
    int status = 0;
    std::string body;
    bool success = false;
    std::string error;
};

class HttpClient {
public:
    explicit HttpClient(const std::string& baseUrl, int timeoutSeconds = 30)
        : m_baseUrl(baseUrl), m_timeoutSeconds(timeoutSeconds) {

        m_logger = spdlog::get("HttpClient");
        if (!m_logger) {
            m_logger = spdlog::stdout_color_mt("HttpClient");
        }

        // Parse URL to extract host and determine HTTPS
        if (baseUrl.find("https://") == 0) {
            m_host = baseUrl.substr(8);
            m_useSSL = true;
        } else if (baseUrl.find("http://") == 0) {
            m_host = baseUrl.substr(7);
            m_useSSL = false;
        } else {
            m_host = baseUrl;
            m_useSSL = false;
        }

        // Remove trailing path
        auto slashPos = m_host.find('/');
        if (slashPos != std::string::npos) {
            m_host = m_host.substr(0, slashPos);
        }
    }

    void setHeader(const std::string& key, const std::string& value) {
        m_headers[key] = value;
    }

    void setBearerToken(const std::string& token) {
        m_headers["Authorization"] = "Bearer " + token;
    }

    HttpResponse post(const std::string& path, const json& body) {
        HttpResponse response;

        try {
            // Create client with full URL scheme for proper HTTPS support
            std::string url = (m_useSSL ? "https://" : "http://") + m_host;
            httplib::Client client(url);

            // Only enable certificate verification if OpenSSL support is compiled in
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
            if (m_useSSL) {
                client.enable_server_certificate_verification(true);
            }
#endif

            client.set_connection_timeout(m_timeoutSeconds);
            client.set_read_timeout(m_timeoutSeconds);
            client.set_write_timeout(m_timeoutSeconds);

            httplib::Headers headers;
            headers.emplace("Content-Type", "application/json");
            for (const auto& [key, value] : m_headers) {
                headers.emplace(key, value);
            }

            std::string bodyStr = body.dump();
            m_logger->debug("POST {} ({} bytes)", path, bodyStr.size());

            auto result = client.Post(path, headers, bodyStr, "application/json");

            if (result) {
                response.status = result->status;
                response.body = result->body;
                response.success = (result->status >= 200 && result->status < 300);

                if (!response.success) {
                    response.error = "HTTP " + std::to_string(result->status);
                    m_logger->warn("HTTP {} for {}: {}", result->status, path,
                                  result->body.substr(0, 200));
                }
            } else {
                response.error = httplib::to_string(result.error());
                m_logger->error("HTTP request failed: {}", response.error);
            }

        } catch (const std::exception& e) {
            response.error = e.what();
            m_logger->error("HTTP exception: {}", e.what());
        }

        return response;
    }

    HttpResponse postMultipart(const std::string& path,
                               const httplib::MultipartFormDataItems& items) {
        HttpResponse response;

        try {
            std::string url = (m_useSSL ? "https://" : "http://") + m_host;
            httplib::Client client(url);

            // Only enable certificate verification if OpenSSL support is compiled in
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
            if (m_useSSL) {
                client.enable_server_certificate_verification(true);
            }
#endif

            client.set_connection_timeout(m_timeoutSeconds);
            client.set_read_timeout(m_timeoutSeconds);

            httplib::Headers headers;
            for (const auto& [key, value] : m_headers) {
                headers.emplace(key, value);
            }

            auto result = client.Post(path, headers, items);

            if (result) {
                response.status = result->status;
                response.body = result->body;
                response.success = (result->status >= 200 && result->status < 300);
            } else {
                response.error = httplib::to_string(result.error());
            }

        } catch (const std::exception& e) {
            response.error = e.what();
        }

        return response;
    }

    HttpResponse get(const std::string& path) {
        HttpResponse response;

        try {
            std::string url = (m_useSSL ? "https://" : "http://") + m_host;
            httplib::Client client(url);

            // Only enable certificate verification if OpenSSL support is compiled in
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
            if (m_useSSL) {
                client.enable_server_certificate_verification(true);
            }
#endif

            client.set_connection_timeout(m_timeoutSeconds);
            client.set_read_timeout(m_timeoutSeconds);

            httplib::Headers headers;
            for (const auto& [key, value] : m_headers) {
                headers.emplace(key, value);
            }

            auto result = client.Get(path, headers);

            if (result) {
                response.status = result->status;
                response.body = result->body;
                response.success = (result->status >= 200 && result->status < 300);
            } else {
                response.error = httplib::to_string(result.error());
            }

        } catch (const std::exception& e) {
            response.error = e.what();
        }

        return response;
    }

private:
    std::string m_baseUrl;
    std::string m_host;
    bool m_useSSL = false;
    int m_timeoutSeconds;
    std::map<std::string, std::string> m_headers;
    std::shared_ptr<spdlog::logger> m_logger;
};

} // namespace celuna
