/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.core;

import de.steamwar.sql.SWException;
import de.steamwar.sql.internal.Statement;
import org.bukkit.Bukkit;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

public class CrashDetector {

    private static final long TIMEOUT = 20_000_000_000L;
    private final AtomicLong lastTick = new AtomicLong(Long.MAX_VALUE);
    private final AtomicLong lastMessage = new AtomicLong(Long.MAX_VALUE);
    private final Thread mainThread = Thread.currentThread();
    private final Thread watchdog;

    private boolean run = true;

    public CrashDetector() {
        Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
            lastTick.set(System.nanoTime());
            lastMessage.set(System.nanoTime());
        }, 0, 1);
        watchdog = new Thread(this::run, "SteamWar Watchdog");
        watchdog.setDaemon(true);
        watchdog.start();
    }

    public void stop() {
        run = false;
        watchdog.interrupt();
    }

    public boolean onMainThread() {
        return Thread.currentThread() == mainThread;
    }

    private void run() {
        SWException.init();
        while (run) {
            long curTime = System.nanoTime();
            if (curTime - 4 * TIMEOUT >= lastTick.get()) {
                SWException.log("Server did not recover in " + ((curTime - lastTick.get()) / 1000000.0) + "ms, unclean server stop", "");
                hardStop();
            } else if (curTime - TIMEOUT > lastMessage.get()) {
                if (mainThread.isAlive()) {
                    SWException.log("Server hung for " + ((curTime - lastTick.get()) / 1000000.0) + "ms", Arrays.stream(mainThread.getStackTrace()).map(StackTraceElement::toString).collect(Collectors.joining("\n")));
                } else {
                    SWException.log("Server thread already dead, unclean server stop", "Core enabled: " + Core.getInstance().isEnabled());
                    hardStop();
                }
                lastMessage.set(curTime);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void hardStop() {
        if (Core.getInstance().isEnabled()) {
            Core.getInstance().onDisable();
        }
        Statement.closeAll();
        //System.exit(0); Does freeze, potential freezing issues: ConsoleRestoreHook, ApplicationShutdownHooks or DeleteOnExitHook
        Runtime.getRuntime().halt(0);
    }
}
