#include "runtime_supervisor.h"

#include <cerrno>
#include <chrono>
#include <csignal>
#include <cstring>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <thread>
#include <unistd.h>

namespace app {
namespace {

volatile std::sig_atomic_t child_pid = -1;
volatile std::sig_atomic_t stopping = 0;

void on_signal(int signal_number){
    stopping = 1;
    const pid_t child = static_cast<pid_t>(child_pid);
    if(child > 0) kill(child, signal_number);
}

} // namespace

int run_supervised(int argc, char **argv, ApplicationEntry entry){
    signal(SIGINT, on_signal);
    signal(SIGTERM, on_signal);

    while(!stopping){
        const pid_t child = fork();
        if(child < 0){
            std::cerr << "[supervisor] fork failed: " << std::strerror(errno) << "\n";
            return 70;
        }
        if(child == 0) return entry(argc, argv);

        child_pid = child;
        int status = 0;
        while(waitpid(child, &status, 0) < 0){
            if(errno == EINTR) continue;
            std::cerr << "[supervisor] waitpid failed: " << std::strerror(errno) << "\n";
            return 70;
        }
        child_pid = -1;
        if(stopping) return 0;

        if(WIFEXITED(status) && WEXITSTATUS(status) == 75){
            std::cerr << "[supervisor] signaling worker stalled; restarting in 2s\n";
            std::this_thread::sleep_for(std::chrono::seconds(2));
            continue;
        }
        if(WIFEXITED(status)) return WEXITSTATUS(status);
        if(WIFSIGNALED(status)) return 128 + WTERMSIG(status);
        return 70;
    }
    return 0;
}

} // namespace app
