#include <flags-cpp/flags-cpp.h>
#include <graph/plugins/http/http.h>
#include <graph/plugins/plugin.h>
#include <rum/http/response.h>
#include <rum/http/server.h>
#include <rum/http/uri.h>
#include <rum/tcp/error.h>
#include <iostream>
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/json.hpp>
#include <sstream>
#include <vector>

using namespace Rum::HTTP;
using json = nlohmann::json;
using namespace Flags;

Server* server;

extern "C" {
plugin_t plugin = {.version = 1, .run_method = &run, .destroy_method = &destroy, .update_callback = nullptr, .argc = 0, .argv = nullptr};

int run() {
  try {
    Parser parser;
    int* port = parser.add<int>("port", "http port", false, 8080);
    int* workers = parser.add<int>("workers", "number of worker threads", false, 10);

    bool* help = parser.add<bool>("help", "print help", false, false);

    if (!parser.parse(plugin.argc, plugin.argv) || *help) {
      parser.help();
    }

    server = new Server(*port, *workers, DEFAULT_BUFFER_SIZE);
    server->add_path<GET>("/add", [](const Request& req, Response& resp) {
      URI uri = req.copy_uri();

      auto params = uri.get_parameters();

      int chart = 0;
      if (params.contains("chart"))
        chart = std::stoi(params.at("chart"));

      std::string series = "1st";
      if (params.contains("series"))
        series = params.at("series");

      if (params.contains("x") && params.contains("y")) {
        point_t point;

        point.x = std::stod(params.at("x"));
        point.y = std::stod(params.at("y"));
        plugin.update_callback(chart, series.c_str(), &point, 1);
      }
    });

    server->add_path<POST>("/add", [](const Request& req, Response& resp) {
      try {
        json data = json::parse(req.get_body());
        if (!data.contains("chart") || !data["chart"].is_number_integer() || !data.contains("series") || !data["series"].is_string() ||
            !data.contains("points") || !data["points"].is_array()) {
          resp.set_code(400);
          return;
        }

        std::vector<point_t> points;
        for (auto point : data["points"]) {
          if (!point.is_object() || !point.contains("x") || !point.contains("y") || !point["x"].is_number() || !point["y"].is_number())
            continue;

          points.push_back({.x = point["x"].template get<double>(), .y = point["y"].template get<double>()});
        }

        plugin.update_callback(data["chart"].template get<int>(), data["series"].template get<std::string>().c_str(), points.data(), points.size());
      } catch (nlohmann::detail::parse_error& e) {
        std::cerr << e.what() << std::endl;
      }
    });

    std::cout << "starting http server" << std::endl;
    server->listen();

    return 0;
  } catch (Rum::TCP::Error& e) {
    std::cerr << e.what() << std::endl;
    delete server;
    return 1;
  }
}

int destroy() {
  if (server)
    server->end();
  std::cout << "stopped http server" << std::endl;
  return 0;
}
}