/*
 *   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.Reflection;
import de.steamwar.sql.SWException;
import org.spigotmc.WatchdogThread;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class ErrorHandler extends Handler {

    private final long watchdogThreadId;

    public ErrorHandler() {
        Logger.getLogger("").addHandler(this);

        Reflection.Field<WatchdogThread> getInstance = Reflection.getField(WatchdogThread.class, WatchdogThread.class, 0);
        watchdogThreadId = getInstance.get(null).getId();
    }

    void unregister() {
        Logger.getLogger("").removeHandler(this);
    }

    @Override
    @SuppressWarnings("deprecation")
    public void publish(LogRecord logRecord) {
        if (logRecord.getLevel().intValue() < Level.WARNING.intValue()) return;

        if (watchdogThreadId == logRecord.getThreadID()) return;

        String message = logRecord.getMessage() != null ? logRecord.getMessage() : "";
        for (String reason : ignoreStartsWith) {
            if (message.startsWith(reason)) return;
        }
        for (String reason : ignoreContains) {
            if (message.contains(reason)) return;
        }

        ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
        if (logRecord.getThrown() != null) {
            logRecord.getThrown().printStackTrace(new PrintStream(stacktraceOutput));
        }
        String stacktrace = stacktraceOutput.toString();
        if (stacktrace.contains("POI data mismatch") || stacktrace.contains("Newer version! Server downgrades are not supported!")) {
            return;
        }

        if (message.isEmpty() && stacktrace.isEmpty()) {
            return;
        }

        try {
            SWException.log(message, stacktrace);
        } catch (SecurityException e) {
            Core.getInstance().getLogger().log(Level.INFO, "Could not log error in database", e);
        }
    }

    @Override
    public void flush() {
        //This is task of the database
    }

    @Override
    public void close() throws SecurityException {
        //Done in the database
    }

    private static final List<String> ignoreStartsWith;
    private static final List<String> ignoreContains;

    static {
        List<String> startsWith = new ArrayList<>();
        startsWith.add("Could not save the list after adding a user.");
        startsWith.add("Could not save spigot.yml");
        startsWith.add("Failed to save operators list:");
        startsWith.add("Block at");
        startsWith.add("POI data mismatch");
        startsWith.add("handleDisconnection() called twice");
        startsWith.add("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
        startsWith.add("The server will make no attempt to authenticate usernames. Beware.");
        startsWith.add("Whilst this makes it possible to use BungeeCord,");
        startsWith.add("Please see http://www.spigotmc.org/wiki/firewall-guide/ for further information.");
        startsWith.add("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
        startsWith.add("This crash report has been saved to:");
        startsWith.add("Could not pass event PlayerQuitEvent to WorldEditSUI");
        startsWith.add("[ViaVersion] ");
        startsWith.add("[ViaBackwards] ");
        startsWith.add("Something went wrong upgrading!");
        startsWith.add("Tried to load unrecognized recipe");
        startsWith.add("Invalid BlockState in palette:");
        startsWith.add("Could not register alias");
        startsWith.add("Can't keep up! Is the server overloaded?");
        startsWith.add("\tat ");
        startsWith.add("java.lang.Exception");
        startsWith.add("An exceptionCaught()");
        startsWith.add("Exception verifying");
        startsWith.add("[WorldEditSUI]");
        startsWith.add("Unsupported key:");
        startsWith.add("ThrownPotion entity");
        startsWith.add("Couldn't load custom particle");
        startsWith.add("Chunk file at [");
        startsWith.add("Ignoring unknown attribute");
        startsWith.add("Skipping player strafe phase because no player was found");
        startsWith.add("Couldn't save chunk; already in use by another instance of Minecraft?");
        startsWith.add("Failed to save player data for ");
        startsWith.add("Failed to check session lock for world located at");
        startsWith.add("Saving oversized chunk ");
        startsWith.add("Ignoring plugin channel");
        startsWith.add("Ignoring incoming plugin");
        startsWith.add("This version of Minecraft is extremely outdated");
        startsWith.add("Custom worlds heights are NOT SUPPORTED for 1.16 players and older");
        startsWith.add("You have min/max set to -64/384");
        startsWith.add("You have min/max set to -64/256");
        startsWith.add("This version of ViaBackwards does not fully support 1.19 servers.");
        startsWith.add("Biome with id");
        startsWith.add("1.16 and 1.16.1 clients are only partially supported");
        startsWith.add("Unable to parse CustomName from ");
        startsWith.add("java.util.ConcurrentModificationException");
        startsWith.add("com.destroystokyo.paper.exception.ServerInternalException: Attempted to place a tile entity");
        startsWith.add("World: minecraft:overworld");
        startsWith.add("Chunk coordinates: ");
        startsWith.add("Failed to save history");
        startsWith.add("\t... ");
        startsWith.add("ERROR IN Protocol");
        ignoreStartsWith = Collections.unmodifiableList(startsWith);

        List<String> contains = new ArrayList<>();
        contains.add("moved too quickly!");
        contains.add("moved wrongly!");
        contains.add("was kicked for floating too long!");
        contains.add("just tried to change non-editable sign");
        contains.add("about their usage of System.out/err.print");
        ignoreContains = Collections.unmodifiableList(contains);
    }
}
