#include <boost/dll.hpp>
#include <spdlog/spdlog.h>

#include "internal/argh.h"
#include "internal/mana_analysis.h" // ManaCountingSink
#include "internal/mana_api.hpp"
#include "internal/mana_nng.hpp"

#include <iostream>

using namespace mesytec;
using namespace mesytec::mnode;

void usage(const char *self)
{
    std::cout << fmt::format("usage: {} [--plugin <plugin.so>] [--plugin-args <args>] <url>\n",
                             self);
    std::cout

        << "  --plugin <plugin.so>            Load a plugin to process the data. Default is a "
           "simple counting plugin.\n"

        << " --plugin-args <args>             Optional arguments to pass to the plugin.\n"

        << "  --log-level=<level>             One of 'trace', 'debug', 'info', 'warn', 'error', "
           "'off'.\n";
}

void run(nng_socket socket_, mana::IManaSink *sink, std::atomic<bool> &quit)
{
    mana::nng_client_run(socket_, sink, quit);
}

int main(int argc, char *argv[])
{
    argh::parser parser(
        {"-h", "--help", "--plugin", "--plugin-args", "--plugin-args", "--log-level"});
    parser.parse(argc, argv);

    auto url = parser[1];

    if (parser["-h"] || parser["--help"] || url.empty())
    {
        usage(argv[0]);
        return 0;
    }

    spdlog::set_level(spdlog::level::info);

    if (parser("--log-level"))
    {
        spdlog::set_level(spdlog::level::from_str(parser("--log-level").str()));
    }

    mana::PluginWrapper plugin;

    if (parser("--plugin"))
    {
        auto pluginFile = parser("--plugin").str();

        try
        {
            plugin = mana::load_mana_plugin(pluginFile, parser("--plugin-args").str());
        }
        catch (const std::exception &e)
        {
            std::cerr << fmt::format("Error loading mana plugin from {}: {}\n", pluginFile,
                                     e.what());
            return 1;
        }
    }
    else
    {
        plugin.destSink = std::make_unique<mana::ManaCountingSink>();
    }

    auto &destSink = plugin.destSink;

    auto manaSink = std::make_unique<mana::ManaSinkPerfProxy>(destSink.get());
    nng_socket socket = nng::make_pair_socket();

    if (int res = nng_dial(socket, url.c_str(), nullptr, 0))
    {
        nng::mnode_nng_error("nng_dial", res);
        return 1;
    }

    spdlog::info("connected to {}", url);
    std::atomic<bool> quit = false;

    run(socket, manaSink.get(), quit);

    fmt::print("Destination Sink: {}\n", to_string(manaSink->perf()));

    return 0;
}
