#include <drogon/drogon.h>
#include <ixwebsocket/IXWebSocket.h>
#include <iostream>
#include <thread>
#include <string>
#include <future>

#include "messaging/messageHandler/messageHandler.h"
#include "nodes/ormConfigurator/ormConfigurator.h"
#include "nodes/reConfigurator/reConfigurator.h"
#include "messaging/messageCounter/messageCounter.h"
#include "nodes/reConfigurator/reHttpPoller/reHttpPoller.h"


bool RunWS_init(std::string& url, const std::string& inti_message, ormNode *orm, GlobalCounter *msgCounter)
{
    bool result = false;
    std::promise<void> finishPromise;
    auto finishFuture = finishPromise.get_future();

    // настраиваем webSocket
    ix::WebSocket webSocket;
    webSocket.setUrl(url);
    std::string endpoint = getEndpointFromUrl(url);

    // настройка callback
    webSocket.setOnMessageCallback([&](const ix::WebSocketMessagePtr &msg) {
        if (msg->type == ix::WebSocketMessageType::Open) {
            try{
                std::cout << "Соединение c " + url + " установлено\n";
                webSocket.send(inti_message);
                std::cout << "Отправлено сообщение: " + inti_message << std::endl;
            }
            catch (...){
                std::cerr << "Неизвестное исключение!" << std::endl;
                finishPromise.set_value();
                result = false;
            }
        }
        else if (msg->type == ix::WebSocketMessageType::Message) {
            try{
                std::cout << "Получено сообщение: " << msg->str << std::endl;
                msgCounter->increment_and_get();

                auto message_json = nlohmann::json::parse(msg->str);
                msgHandler handler(message_json, url);
                handler.msgHandle(orm);

                finishPromise.set_value();
                result = true;
            }
            catch (const std::exception &e) {
                std::cerr << e.what() << std::endl;
                finishPromise.set_value();
                result = true;
            }
        }
        else if (msg->type == ix::WebSocketMessageType::Error) {
            std::cerr << "Ошибка: " << msg->errorInfo.reason << std::endl;
            finishPromise.set_value();
            result = false;
        }

    });
    webSocket.start();
    finishFuture.wait();
    webSocket.stop();
    return result;
}


bool RunWS(std::string& url, const std::string& inti_message, ormNode *orm, GlobalCounter *msgCounter){

    bool result = false;
    std::promise<void> finishPromise;
    auto finishFuture = finishPromise.get_future();

    ix::WebSocket webSocket;
    webSocket.setUrl(url);
    std::string endpoint = getEndpointFromUrl(url);
    webSocket.setPingInterval(1);
    webSocket.enableAutomaticReconnection();

    webSocket.setOnMessageCallback([&](const ix::WebSocketMessagePtr& msg) {
        if (msg->type == ix::WebSocketMessageType::Open)
        {
            std::cout << "Соединение c " + url + " установлено\n";
            webSocket.send(inti_message);
            std::cout << "Отправлено сообщение: " + inti_message << std::endl;
            msgCounter->increment_and_get();

        }
        else if (msg->type == ix::WebSocketMessageType::Message)
        {
            std::cout << "Получено сообщение: " << msg->str << std::endl;
            auto message_json = nlohmann::json::parse(msg->str);
            msgHandler handler(message_json, url);
            handler.msgHandle(orm);
            result = true;

        }
        else if (msg->type == ix::WebSocketMessageType::Error)
        {
            std::cerr << "Ошибка websocket на " + endpoint + ": " << msg->errorInfo.reason << std::endl;
            result = false;
        }
    });
    webSocket.start();
    finishFuture.wait();
    webSocket.stop();
    return result;
}


int main() {
    // формируем основные сущности
    ormNode orm;
    reNode re;
    GlobalCounter msgCounter;
    reHttpPoller reHttpPoller("http://re.test.promuc.local/api/version", 60000);

    // запускаем
    reHttpPoller.start();

    //////////////////////////////////////////
    // соединение с БД re в reHttpPoller, заполняем поля reNode//
    // получаем данные о RE //
    re._params.re_peer = "re.local";
    re._params.re_version = "1.2.1";
    re._params.re_opc_server_peer = "opc.re.local";
    re._params.re_location = "default";
    ///////////////////////////////////////////
    std::string inti_messages[] = {
            "{ \"re_peer\": \"re.promuc.local\", \"re_version\": \"1.0.1\", \"re_opc_server_peer\": \"re.opc.promuc.local\", \"count_message\": 1, \"count_attempts\": 3, \"count_tags_conc\": 0, \"timeout_waiting_response\": 1000, \"status\": \"init_INIT_AAA\" }",
            "{ \"re_peer\": \"re.promuc.local\", \"re_version\": \"1.0.1\", \"re_opc_server_peer\": \"re.opc.promuc.local\", \"count_message\": 3, \"count_attempts\": 3, \"count_tags_conc\": 0, \"timeout_waiting_response\": 1000, \"status\": \"init_INIT_LOCATION\" }",
            "{ \"re_peer\": \"re.promuc.local\", \"re_version\": \"1.0.1\", \"re_opc_server_peer\": \"re.opc.promuc.local\", \"count_message\": 7, \"count_attempts\": 3, \"count_tags_conc\": 0, \"timeout_waiting_response\": 1000, \"status\": \"init_INIT_CHAIN\" }"};

    std::string fake_re_data[] = {

    };
    std::string SD_endpoints[] = {"ws://localhost:8080/SC_SD_F1",
                                  "ws://localhost:8080/SC_SD_F2",
                                  "ws://localhost:8080/SC_SD_F3"};
    ////////////////////////////////////////////


    bool ws_conn_result = RunWS_init(SD_endpoints[0], inti_messages[0], &orm, &msgCounter);
    while (!ws_conn_result) {
        std::this_thread::sleep_for(std::chrono::seconds(3));
        ws_conn_result = RunWS_init(SD_endpoints[0], inti_messages[0], &orm, &msgCounter);
    }

    std::thread locations_thread([&]() {
        if (orm._params.init_status == "init_OK_AAA") {
            bool ws_conn_result = RunWS(SD_endpoints[1], inti_messages[1], &orm, &msgCounter);
            while (!ws_conn_result) {
                std::this_thread::sleep_for(std::chrono::seconds(3));
                ws_conn_result = RunWS(SD_endpoints[1], inti_messages[1], &orm, &msgCounter);
            }
        }
        else std::cout << "BAD: orm.init_status is not OK" << std::endl;
    });

    std::thread chains_thread([&]() {
        std::unique_lock<std::mutex> lock(orm.status_mutex);
        orm.status_cv.wait(lock, [&] {
            return orm.status_ready;
        });
        bool ready_to_start_F3_ws = (orm._params.location_status == "init_OK_LOCATION");
        lock.unlock();

        if (ready_to_start_F3_ws) {
            bool ws_conn_result = RunWS(SD_endpoints[2], inti_messages[2], &orm, &msgCounter);
            while (!ws_conn_result) {
                std::this_thread::sleep_for(std::chrono::seconds(3));
                ws_conn_result = RunWS(SD_endpoints[2], inti_messages[2], &orm, &msgCounter);
            }
        }
        else std::cout << "BAD: orm.location_status is not OK" << std::endl;
    });

    drogon::app().addListener("0.0.0.0", 4343);
    drogon::app().run();
    locations_thread.join();
    chains_thread.join();
    return 0;
}