#include "core/entrypoint.hpp"

#include "cli/cli.hpp"
#include "core/application.hpp"
#include "core/engine.hpp"
#include "logging/engine_logger.hpp"

#include <cstdlib>
#include <exception>
#include <memory>
#include <utility>

// Implemented by the user
extern std::unique_ptr<kuiper::application> kuiper::create_application(int argc, char* argv[]);

int main(int argc, char* argv[]) {
    return kuiper::entrypoint(argc, argv);
}

int kuiper::entrypoint(int argc, char* argv[]) {
    // Initialise logging

    auto logger = engine_logger::get();

    // Parse command line arguments

    // NOTE: We assume that command line arguments are UTF-8 encoded at this point
    const auto maybe_args = cli::try_construct(argc, argv);
    if (!maybe_args) {
        logger.error("Invalid command like argument: {}", cli::cli_args_parse_err_str(maybe_args.error()));
        return EXIT_FAILURE;
    }
    const auto& cli_args = maybe_args.value();

    // Create instance of client application

    std::unique_ptr<kuiper::application> app = kuiper::create_application(argc, argv);

    if (app == nullptr) {
        logger.critical("Application was nullptr");
        return EXIT_FAILURE;
    }

    // Initialise engine & begin execution

    try {
        const auto initialised = engine::initialise(cli_args);
        if (!initialised) {
            logger.critical("Failed to initialise engine");
            return EXIT_FAILURE;
        }

        engine engine(std::move(app));
        engine.start();
    } catch (const std::exception& ex) {
        logger.critical("Unhandled std::exception: {}", ex.what());
        return EXIT_FAILURE;
    } catch (...) {
        logger.critical("Unknown unhandled exception!");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS; // TODO: Return error exit code if the app encounters an error during execution
}
