package io.xpipe.app.util;

import io.xpipe.app.core.AppNames;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.process.ShellDialects;

import org.apache.commons.lang3.exception.ExceptionUtils;

import java.nio.file.Files;
import java.nio.file.Path;

public class Deobfuscator {

    public static String deobfuscateToString(Throwable t) {
        String stackTrace = ExceptionUtils.getStackTrace(t);
        stackTrace = stackTrace.replaceAll("\tat .+/(.+)", "\tat $1");

        try {
            if (!canDeobfuscate()) {
                return stackTrace;
            }

            var file = Files.createTempFile(AppNames.ofCurrent().getKebapName() + "-stacktrace", null);
            Files.writeString(file, stackTrace);
            var proc = new ProcessBuilder(
                            "retrace." + (OsType.ofLocal() == OsType.WINDOWS ? "bat" : "sh"),
                            System.getenv(AppNames.ofMain().getUppercaseName() + "_MAPPING"),
                            file.toString())
                    .redirectErrorStream(true);
            var active = proc.start();
            var out = new String(active.getInputStream().readAllBytes()).replaceAll("\r\n", "\n");
            var code = active.waitFor();
            if (code == 0) {
                return out;
            } else {
                System.err.println("Deobfuscation failed: " + out);
            }
        } catch (Exception ex) {
            System.err.println("Deobfuscation failed");
            return stackTrace;
        }

        return stackTrace;
    }

    private static boolean canDeobfuscate() {
        if (AppProperties.get().isDevelopmentEnvironment()) {
            return false;
        }

        if (!System.getenv().containsKey("XPIPE_MAPPING")) {
            return false;
        }

        var file = Path.of(System.getenv("XPIPE_MAPPING"));
        if (!Files.exists(file)) {
            return false;
        }

        // We probably can't run .bat scripts in this case
        if (OsType.ofLocal() == OsType.WINDOWS
                && ProcessControlProvider.get().getEffectiveLocalDialect() != ShellDialects.CMD) {
            return false;
        }

        if (OsType.ofLocal() == OsType.WINDOWS) {
            var reg = WindowsRegistry.local()
                    .readStringValueIfPresent(
                            WindowsRegistry.HKEY_CURRENT_USER,
                            "Software\\Policies\\Microsoft\\Windows\\System",
                            "DisableCMD");
            if (reg.isPresent() && reg.get().equals("1")) {
                return false;
            }
        }

        return true;
    }
}
