#include "consul.h"

#include "HttpClient.h"
using namespace hv;

#include "json.hpp"
using json = nlohmann::json;

#define PROTOCOL    "http://"
#define API_VERSION "v1"

static const char url_register[] = "/agent/service/register";
static const char url_deregister[] = "/agent/service/deregister";
static const char url_discover[] = "/catalog/service";

static std::string make_url(const char* ip, int port, const char* url) {
    return asprintf(PROTOCOL "%s:%d/" API_VERSION "%s", ip, port, url);
}

static std::string make_ServiceID(consul_service_t* service) {
    return asprintf("%s-%s:%d", service->name, service->ip, service->port);
}

/*
{
  "ID": "redis1",
  "Name": "redis",
  "Tags": [
    "primary",
    "v1"
  ],
  "Address": "127.0.0.1",
  "Port": 8000,
  "Meta": {
    "redis_version": "4.0"
  },
  "EnableTagOverride": false,
  "Check": {
    "DeregisterCriticalServiceAfter": "90m",
    "Args": ["/usr/local/bin/check_redis.py"],
    "HTTP": "http://localhost:5000/health",
    "Interval": "10s",
    "TTL": "15s"
  },
  "Weights": {
    "Passing": 10,
    "Warning": 1
  }
}
 */
int register_service(consul_node_t* node, consul_service_t* service, consul_health_t* health) {
    HttpRequest req;
    req.method = HTTP_PUT;
    req.url = make_url(node->ip, node->port, url_register);
    req.content_type = APPLICATION_JSON;

    json jservice;
    jservice["Name"] = service->name;
    if (*service->ip) {
        jservice["Address"] = service->ip;
    }
    jservice["Port"] = service->port;
    jservice["ID"] = make_ServiceID(service);

    json jcheck;
    if (*health->url == '\0') {
        snprintf(health->url, sizeof(health->url), "%s:%d", service->ip, service->port);
    }
    jcheck[health->protocol] = health->url;
    jcheck["Interval"] = asprintf("%dms", health->interval);
    jcheck["DeregisterCriticalServiceAfter"] = asprintf("%dms", health->interval * 3);
    jservice["Check"] = jcheck;

    req.body = jservice.dump();
    printd("PUT %s\n", req.url.c_str());
    printd("%s\n", req.body.c_str());

    HttpResponse res;
    int ret = http_client_send(&req, &res);
    printd("%s\n", res.body.c_str());
    return ret;
}

int deregister_service(consul_node_t* node, consul_service_t* service) {
    std::string url = make_url(node->ip, node->port, url_deregister);
    url += '/';
    url += make_ServiceID(service);

    HttpRequest req;
    req.method = HTTP_PUT;
    req.url = url;
    req.content_type = APPLICATION_JSON;
    printd("PUT %s\n", req.url.c_str());

    HttpResponse res;
    int ret = http_client_send(&req, &res);
    printd("%s\n", res.body.c_str());
    return ret;
}

int discover_services(consul_node_t* node, const char* service_name, std::vector<consul_service_t>& services) {
    std::string url = make_url(node->ip, node->port, url_discover);
    url += '/';
    url += service_name;

    HttpRequest req;
    req.method = HTTP_GET;
    req.url = url;

    HttpResponse res;
    printd("GET %s\n", req.url.c_str());
    int ret = http_client_send(&req, &res);
    if (ret != 0) return ret;
    printd("%s\n", res.body.c_str());

    json jroot = json::parse(res.body);
    if (!jroot.is_array()) return -1;
    if (jroot.size() == 0) return 0;

    consul_service_t service;
    std::string name, ip;
    services.clear();
    for (size_t i = 0; i < jroot.size(); ++i) {
        auto jservice = jroot[i];
        name = jservice["ServiceName"];
        if (jservice.contains("Address")) {
            ip = jservice["Address"];
        } else if (jservice.contains("ServiceAddress")) {
            ip = jservice["ServiceAddress"];
        } else if (jservice.contains("ServiceAddress6")) {
            ip = jservice["ServiceAddress6"];
        } else {
            continue;
        }
        int port = jservice["ServicePort"];

        strncpy(service.name, name.c_str(), sizeof(service.name));
        strncpy(service.ip, ip.c_str(), sizeof(service.ip));
        service.port = port;
        services.emplace_back(service);
    }

    return 0;
}
