#include "mqtt_config_service.h"

#include <curl/curl.h>
#include <nlohmann/json.hpp>

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>

namespace mqtt {
namespace {

constexpr const char *kConfigEndpoint =
    "https://fcrs-api.yd-ss.com/device/getConfig?deviceNo=";
constexpr const char *kFallbackBroker = "mqtt177.controlmelive.com";
constexpr std::size_t kMaxBrokers = 15;

size_t append_response(char *data, size_t size, size_t count, void *userdata){
    const size_t bytes = size * count;
    if(!userdata || !data) return 0;
    static_cast<std::string *>(userdata)->append(data, bytes);
    return bytes;
}

void add_broker(RemoteConfig &config, const std::string &host){
    if(host.empty() || config.brokers.size() >= kMaxBrokers) return;
    if(std::find(config.brokers.begin(), config.brokers.end(), host) ==
       config.brokers.end()){
        config.brokers.push_back(host);
    }
}

RemoteConfig fallback_config(){
    RemoteConfig config;
    config.brokers.emplace_back(kFallbackBroker);
    return config;
}

} // namespace

RemoteConfig ConfigService::fetch(const std::string &device_id) const{
    RemoteConfig config = fallback_config();
    if(curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK){
        std::cerr << "[mqtt-config] curl global initialization failed; using fallback broker\n";
        return config;
    }

    std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl(
        curl_easy_init(), &curl_easy_cleanup);
    if(!curl){
        curl_global_cleanup();
        std::cerr << "[mqtt-config] curl initialization failed; using fallback broker\n";
        return config;
    }

    char *escaped = curl_easy_escape(curl.get(), device_id.c_str(),
                                     static_cast<int>(device_id.size()));
    if(!escaped){
        curl.reset();
        curl_global_cleanup();
        std::cerr << "[mqtt-config] cannot encode device id; using fallback broker\n";
        return config;
    }
    const std::string url = std::string(kConfigEndpoint) + escaped;
    curl_free(escaped);

    std::string response;
    char curl_error[CURL_ERROR_SIZE] = {0};
    curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, curl_error);
    curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, append_response);
    curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);
    curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "webrtcpush/1.0.1");
    curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT, 5L);
    curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 8L);
    curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curl.get(), CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt(curl.get(), CURLOPT_SSLVERSION,
                     CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2);

    const char *ca_file = std::getenv("WEBRTCPUSH_CA_CERT");
    if(ca_file && *ca_file) curl_easy_setopt(curl.get(), CURLOPT_CAINFO, ca_file);

    std::cerr << "[mqtt-config] GET " << url << "\n";
    const CURLcode request_result = curl_easy_perform(curl.get());
    long http_status = 0;
    curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_status);

    if(request_result != CURLE_OK || http_status != 200){
        std::cerr << "[mqtt-config] request failed: "
                  << (curl_error[0] ? curl_error
                                    : curl_easy_strerror(request_result))
                  << " (HTTP " << http_status << "); using fallback broker\n";
        curl.reset();
        curl_global_cleanup();
        return config;
    }

    try {
        const nlohmann::json root = nlohmann::json::parse(response);
        if(!root.contains("code") || !root["code"].is_number_integer() ||
           root["code"].get<int>() != 200 ||
           !root.contains("data") || !root["data"].is_object()){
            throw std::runtime_error("API code/data is invalid");
        }

        const auto &data = root["data"];
        RemoteConfig fetched;
        if(data.contains("mqtt") && data["mqtt"].is_array()){
            for(const auto &entry : data["mqtt"]){
                if(entry.is_string()) add_broker(fetched, entry.get<std::string>());
            }
        }
        if(fetched.brokers.empty())
            throw std::runtime_error("API returned no MQTT brokers");

        if(data.contains("videoUrlForDevice") &&
           data["videoUrlForDevice"].is_string()){
            fetched.video_url = data["videoUrlForDevice"].get<std::string>();
        }
        fetched.loaded_from_api = true;
        config = std::move(fetched);
    }catch(const std::exception &e){
        std::cerr << "[mqtt-config] invalid response: " << e.what()
                  << "; using fallback broker\n";
    }

    curl.reset();
    curl_global_cleanup();
    for(const auto &broker : config.brokers)
        std::cerr << "[mqtt-config] broker=" << broker << ":1883\n";
    if(!config.video_url.empty())
        std::cerr << "[mqtt-config] videoUrlForDevice=" << config.video_url << "\n";
    return config;
}

} // namespace mqtt
