#include "actions.hpp"
#include "json.hpp"
#include "rpc_server.hpp"
#include "vm.hpp"
#include "vm_provider.hpp"
#include <cstdint>
#include <cstdlib>
#include <format>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

int execute_file_and_exit(std::string filename, bool print_debug)
{
    auto file = std::ifstream();
    file.open(filename.c_str());
    if (!file) {
        std::cout << std::format("error: could not open file '{}'\n", filename);
        return 1;
    }
    auto text = std::string(std::istreambuf_iterator<char> { file }, {});
    auto parsed = sliger::json::parse_json(text);
    if (not parsed.ok()) {
        std::cout << std::format("error: {} at {}:{}\n", parsed.err().msg,
            parsed.err().pos.line, parsed.err().pos.col);
        return 1;
    }
    auto program = std::vector<uint32_t>();
    for (auto& v : parsed.val()->as<sliger::json::Array>().values) {
        program.push_back(
            static_cast<uint32_t>(v->as<sliger::json::Number>().value));
    }
    auto vm = sliger::VM(program,
        {
            .flame_graph = false,
            .code_coverage = false,
            .print_debug = print_debug,
        });
    vm.run_until_done();
    return 0;
}

int main(int argc, char** argv)
{
    if (argc >= 3 && std::string(argv[1]) == "run") {
        auto filename = std::string();
        bool print_debug = false;

        for (int i = 2; i < argc; ++i) {
            auto arg = std::string(argv[i]);
            if (arg == "--print-debug") {
                print_debug = true;
            } else {
                if (!filename.empty()) {
                    std::cerr << std::format("error: >1 files specified\n");
                    std::exit(1);
                }
                filename = arg;
            }
        }
        if (filename.empty()) {
            std::cerr << std::format("error: no file specified\n");
            std::exit(1);
        }

        return execute_file_and_exit(argv[2], print_debug);
    }

    auto vm_provider = sliger::rpc::VmProvider();

    auto rpc = sliger::rpc::RpcServer(
        [&](std::unique_ptr<sliger::json::Value> req,
            std::unique_ptr<sliger::rpc::BufferedWriter> writer) {
            auto action = sliger::rpc::action::action_from_json(*req);
            action->perform_action(std::move(writer), vm_provider);
        });

    std::cout << "Runtime at 127.0.0.1:13370\n";
    auto res = rpc.listen();
    if (!res.is_ok()) {
        std::cout << res.err().msg << "\n";
    }
}
