package eu.mrneznamy.mrultimatelibrary.gui;

import eu.mrneznamy.mrultimatelibrary.MrUltimateLibrary;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.EventExecutor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.logging.Level;

/**
 * Native Paper Dialog implementation using reflection.
 * Handles inputs using correct DialogResponseView methods: getText(), getBoolean(), getFloat().
 */
public class MrLibFormNative {

    private static final Map<UUID, Consumer<Object>> pendingConfirm = new ConcurrentHashMap<>();
    private static final Map<UUID, Runnable> pendingCancel  = new ConcurrentHashMap<>();
    private static final Map<String, Consumer<Object>> inputSubmitHandlers = new ConcurrentHashMap<>();
    private static final Map<String, Runnable>         inputCancelHandlers = new ConcurrentHashMap<>();

    private static boolean eventRegistered = false;

    private static Method findDeserializeMethod(Object serializer) {
        if (serializer == null) return null;
        Class<?> clazz = serializer.getClass();
        
        
        Method m = findMethodInHierarchy(clazz, "deserialize", String.class);
        if (m != null) return m;

        
        try {
            for (Method method : clazz.getMethods()) {
                if (method.getName().equals("deserialize") && method.getParameterCount() == 1 && method.getParameterTypes()[0] == String.class) {
                    try {
                        method.setAccessible(true);
                    } catch (Throwable ignored) {}
                    return method;
                }
            }
        } catch (Throwable ignored) {}
        
        return null;
    }

    private static Method findMethodInHierarchy(Class<?> clazz, String name, Class<?>... params) {
        
        if (java.lang.reflect.Modifier.isPublic(clazz.getModifiers())) {
            try {
                return clazz.getMethod(name, params);
            } catch (NoSuchMethodException ignored) {}
        }
        
        
        for (Class<?> iface : clazz.getInterfaces()) {
            Method m = findMethodInHierarchy(iface, name, params);
            if (m != null) return m;
        }
        
        
        Class<?> superclass = clazz.getSuperclass();
        if (superclass != null && superclass != Object.class) {
            return findMethodInHierarchy(superclass, name, params);
        }
        
        return null;
    }

    
    
    

    public static void open(Player player,
                            String title,
                            List<MrLibForm.FormElement> elements,
                            Consumer<MrLibForm.FormResult> onSubmit,
                            Runnable onCancel) {
        try {
            ClassLoader loader = MrLibFormNative.class.getClassLoader();
            if (loader == null) {
                loader = Bukkit.class.getClassLoader();
            }

            
            Class<?> dialogClass      = Class.forName("io.papermc.paper.dialog.Dialog", true, loader);
            Class<?> dialogBaseClass  = Class.forName("io.papermc.paper.registry.data.dialog.DialogBase", true, loader);
            Class<?> dialogBodyClass  = Class.forName("io.papermc.paper.registry.data.dialog.body.DialogBody", true, loader);
            Class<?> dialogInputClass = Class.forName("io.papermc.paper.registry.data.dialog.input.DialogInput", true, loader);
            Class<?> dialogTypeClass  = Class.forName("io.papermc.paper.registry.data.dialog.type.DialogType", true, loader);
            Class<?> componentClass   = Class.forName("net.kyori.adventure.text.Component", true, loader);
            Class<?> actionButtonClass = Class.forName("io.papermc.paper.registry.data.dialog.ActionButton", true, loader);
            Class<?> consumerClass    = Class.forName("java.util.function.Consumer", true, loader);

            Class<?> dialogActionClass = null;
            for (String pkg : new String[]{
                    "io.papermc.paper.registry.data.dialog.action.DialogAction",
                    "io.papermc.paper.registry.data.dialog.DialogAction"}) {
                try { dialogActionClass = Class.forName(pkg, true, loader); break; }
                catch (ClassNotFoundException ignored) {}
            }

            Class<?> keyClass = null;
            try { keyClass = Class.forName("net.kyori.adventure.key.Key", true, loader); }
            catch (ClassNotFoundException ignored) {}

            
            Object sectionSerializerObj = null;
            Method sectionDeserializeMethod = null;
            try {
                Class<?> legacySerializerClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer", true, loader);
                Class<?> builderClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer$Builder", true, loader);
                Method builderMethod = legacySerializerClass.getMethod("builder");
                Object builderObj = builderMethod.invoke(null);
                
                
                Method characterMethod = builderClass.getMethod("character", char.class);
                builderObj = characterMethod.invoke(builderObj, '§');
                
                
                Method hexColorsMethod = builderClass.getMethod("hexColors");
                builderObj = hexColorsMethod.invoke(builderObj);
                
                
                Method buildMethod = builderClass.getMethod("build");
                sectionSerializerObj = buildMethod.invoke(builderObj);
                sectionDeserializeMethod = findDeserializeMethod(sectionSerializerObj);
            } catch (Throwable e) {
                try {
                    Class<?> legacySerializerClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer", true, loader);
                    Method legacySection = legacySerializerClass.getMethod("legacySection");
                    sectionSerializerObj = legacySection.invoke(null);
                    sectionDeserializeMethod = findDeserializeMethod(sectionSerializerObj);
                } catch (Throwable ignored) {}
            }

            Object ampersandSerializerObj = null;
            Method ampersandDeserializeMethod = null;
            try {
                Class<?> legacySerializerClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer", true, loader);
                Class<?> builderClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer$Builder", true, loader);
                Method builderMethod = legacySerializerClass.getMethod("builder");
                Object builderObj = builderMethod.invoke(null);
                
                
                Method characterMethod = builderClass.getMethod("character", char.class);
                builderObj = characterMethod.invoke(builderObj, '&');
                
                
                Method hexColorsMethod = builderClass.getMethod("hexColors");
                builderObj = hexColorsMethod.invoke(builderObj);
                
                
                Method buildMethod = builderClass.getMethod("build");
                ampersandSerializerObj = buildMethod.invoke(builderObj);
                ampersandDeserializeMethod = findDeserializeMethod(ampersandSerializerObj);
            } catch (Throwable e) {
                try {
                    Class<?> legacySerializerClass = Class.forName("net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer", true, loader);
                    Method legacyAmpersand = legacySerializerClass.getMethod("ampersand");
                    ampersandSerializerObj = legacyAmpersand.invoke(null);
                    ampersandDeserializeMethod = findDeserializeMethod(ampersandSerializerObj);
                } catch (Throwable ignored) {}
            }

            Object miniMessageObj = null;
            Method miniMessageDeserializeMethod = null;
            try {
                Class<?> mmClass = Class.forName("net.kyori.adventure.text.minimessage.MiniMessage", true, loader);
                Method mmMethod = mmClass.getMethod("miniMessage");
                miniMessageObj = mmMethod.invoke(null);
                miniMessageDeserializeMethod = findDeserializeMethod(miniMessageObj);
            } catch (Throwable e) {
                Bukkit.getLogger().warning("[MrUltimateLibrary] Failed to load MiniMessage class: " + e.getMessage());
            }

            Object bungeeSerializerObj = null;
            Method bungeeDeserializeMethod = null;
            try {
                Class<?> bungeeSerializerClass = Class.forName("net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer", true, loader);
                Method getMethod = bungeeSerializerClass.getMethod("get");
                bungeeSerializerObj = getMethod.invoke(null);
                
                Class<?> baseComponentClass = Class.forName("net.md_5.bungee.api.chat.BaseComponent", true, loader);
                Class<?> baseComponentArrayClass = java.lang.reflect.Array.newInstance(baseComponentClass, 0).getClass();
                bungeeDeserializeMethod = bungeeSerializerClass.getMethod("deserialize", baseComponentArrayClass);
            } catch (Throwable ignored) {}

            Method fromLegacyTextMethod = null;
            try {
                Class<?> textComponentClass = Class.forName("net.md_5.bungee.api.chat.TextComponent", true, loader);
                fromLegacyTextMethod = textComponentClass.getMethod("fromLegacyText", String.class);
            } catch (Throwable ignored) {}

            final Method finalFromLegacy = fromLegacyTextMethod;
            final Object finalBungeeSerializer = bungeeSerializerObj;
            final Method finalBungeeDeserialize = bungeeDeserializeMethod;
            final Object finalMM = miniMessageObj;
            final Method finalMMDeserialize = miniMessageDeserializeMethod;

            final Object sectionSerializer = sectionSerializerObj;
            final Method sectionDeserialize = sectionDeserializeMethod;
            final Object ampersandSerializer = ampersandSerializerObj;
            final Method ampersandDeserialize = ampersandDeserializeMethod;
            final Method plainTextMethod = componentClass.getMethod("text", String.class);

            java.util.function.Function<String, Object> createComp = (text) -> {
                if (text == null) return null;
                try {
                    
                    if (finalMM != null && finalMMDeserialize != null) {
                        try {
                            
                            try {
                                Object comp = finalMMDeserialize.invoke(finalMM, text);
                                if (comp != null) return comp;
                            } catch (Throwable ignored) {}

                            
                            String mmString = legacyToMiniMessage(text);
                            Object comp = finalMMDeserialize.invoke(finalMM, mmString);
                            if (comp != null) return comp;
                        } catch (Throwable ignored) {}
                    }

                    String colored = text;
                    try {
                        
                        colored = eu.mrneznamy.mrultimatelibrary.utils.MrLibColors.colorize(text);
                    } catch (Throwable ignored) {}

                    
                    if (finalFromLegacy != null && finalBungeeSerializer != null && finalBungeeDeserialize != null) {
                        try {
                            Object baseComponents = finalFromLegacy.invoke(null, colored);
                            Object comp = finalBungeeDeserialize.invoke(finalBungeeSerializer, baseComponents);
                            if (comp != null) return comp;
                        } catch (Exception ignored) {}
                    }

                    if (sectionDeserialize != null && sectionSerializer != null) {
                        return sectionDeserialize.invoke(sectionSerializer, colored);
                    }
                    if (ampersandDeserialize != null && ampersandSerializer != null) {
                        return ampersandDeserialize.invoke(ampersandSerializer, text.replace("§", "&"));
                    }
                } catch (Exception ignored) {}
                try {
                    return plainTextMethod.invoke(null, text);
                } catch (Exception ignored) {
                    return null;
                }
            };

            Object titleComp = createComp.apply(title);

            
            List<Object> bodies = new ArrayList<>();
            List<Object> inputs = new ArrayList<>();
            boolean hasInputs = false;

            for (MrLibForm.FormElement el : elements) {
                switch (el.type) {
                    case MESSAGE:
                        try {
                            Method m = dialogBodyClass.getMethod("plainMessage", componentClass);
                            bodies.add(m.invoke(null, createComp.apply(el.label)));
                        } catch (Exception ignored) {}
                        break;
                    case ITEM:
                        try {
                            Method m = dialogBodyClass.getMethod("item", ItemStack.class);
                            bodies.add(m.invoke(null, el.data));
                        } catch (Exception ignored) {}
                        break;
                    case TEXT:
                        hasInputs = true;
                        try {
                            Method m = dialogInputClass.getMethod("text", String.class, componentClass);
                            Object b = m.invoke(null, el.id, createComp.apply(el.label));
                            
                            
                            try {
                                Method maxLengthMethod = b.getClass().getMethod("maxLength", int.class);
                                Object returnedBuilder = maxLengthMethod.invoke(b, 5000);
                                if (returnedBuilder != null) b = returnedBuilder;
                            } catch (Exception ignored) {}

                            
                            try {
                                Class<?> multilineOptionsClass = Class.forName("io.papermc.paper.registry.data.dialog.input.TextDialogInput$MultilineOptions", true, loader);
                                Method createMethod = multilineOptionsClass.getMethod("create", int.class, Integer.class);
                                Object options = createMethod.invoke(null, 5, null);
                                Method multilineMethod = b.getClass().getMethod("multiline", multilineOptionsClass);
                                Object returnedBuilder = multilineMethod.invoke(b, options);
                                if (returnedBuilder != null) b = returnedBuilder;
                            } catch (Exception ignored) {}

                            if (el.data != null) {
                                try {
                                    Method initialMethod = b.getClass().getMethod("initial", String.class);
                                    Object returnedBuilder = initialMethod.invoke(b, (String) el.data);
                                    if (returnedBuilder != null) b = returnedBuilder;
                                } catch (Exception e) {
                                    Bukkit.getLogger().warning("[MrUltimateLibrary] Failed to set text default: " + e.getMessage());
                                }
                            }
                            inputs.add(b.getClass().getMethod("build").invoke(b));
                        } catch (Exception e) {
                            Bukkit.getLogger().log(Level.SEVERE, "[MrUltimateLibrary] Failed to build TEXT element: " + el.id, e);
                        }
                        break;
                    case CHECKBOX:
                        hasInputs = true;
                        try {
                            Method m = dialogInputClass.getMethod("bool", String.class, componentClass);
                            Object b = m.invoke(null, el.id, createComp.apply(el.label));
                            if (el.data != null) {
                                try {
                                    Method initialMethod = b.getClass().getMethod("initial", boolean.class);
                                    Object returnedBuilder = initialMethod.invoke(b, (Boolean) el.data);
                                    if (returnedBuilder != null) b = returnedBuilder;
                                } catch (Exception e) {
                                    Bukkit.getLogger().warning("[MrUltimateLibrary] Failed to set boolean default: " + e.getMessage());
                                }
                            }
                            inputs.add(b.getClass().getMethod("build").invoke(b));
                        } catch (Exception e) {
                            Bukkit.getLogger().log(Level.SEVERE, "[MrUltimateLibrary] Failed to build CHECKBOX element: " + el.id, e);
                        }
                        break;
                    case DROPDOWN:
                        hasInputs = true;
                        try {
                            Class<?> optionEntryClass = Class.forName("io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput$OptionEntry", true, loader);
                            Method createEntry = optionEntryClass.getMethod("create", String.class, componentClass, boolean.class);
                            List<Object> entries = new ArrayList<>();
                            List<String> options = (List<String>) el.data;
                            for (int i = 0; i < options.size(); i++) {
                                String opt = options.get(i);
                                Object entry = createEntry.invoke(null, opt, createComp.apply(opt), i == 0);
                                entries.add(entry);
                            }

                            Method m = dialogInputClass.getMethod("singleOption", String.class, componentClass, List.class);
                            Object b = m.invoke(null, el.id, createComp.apply(el.label), entries);
                            inputs.add(b.getClass().getMethod("build").invoke(b));
                        } catch (Exception e) {
                            Bukkit.getLogger().log(Level.SEVERE, "[MrUltimateLibrary] Failed to build DROPDOWN element: " + el.id, e);
                        }
                        break;
                    case SLIDER:
                        hasInputs = true;
                        try {
                            float[] range = (float[]) el.data; 
                            Method m = dialogInputClass.getMethod("numberRange", String.class, componentClass, float.class, float.class);
                            Object b = m.invoke(null, el.id, createComp.apply(el.label), range[0], range[1]);
                            
                            
                            if (range.length > 3) {
                                float step = range[3];
                                try {
                                    Method stepMethod = findMethodInHierarchy(b.getClass(), "step", float.class);
                                    if (stepMethod == null) stepMethod = findMethodInHierarchy(b.getClass(), "step", double.class);
                                    if (stepMethod == null) stepMethod = findMethodInHierarchy(b.getClass(), "step", Number.class);
                                    
                                    if (stepMethod != null) {
                                        try { stepMethod.setAccessible(true); } catch (Throwable ignored) {}
                                        Object returnedBuilder = stepMethod.invoke(b, step);
                                        if (returnedBuilder != null) b = returnedBuilder;
                                    }
                                } catch (Exception e) {
                                    Bukkit.getLogger().warning("[MrUltimateLibrary] Failed to set slider step: " + e.getMessage());
                                }
                            }

                            if (range.length > 2) {
                                try {
                                    Method initialMethod = findMethodInHierarchy(b.getClass(), "initial", float.class);
                                    if (initialMethod == null) initialMethod = findMethodInHierarchy(b.getClass(), "initial", Float.class);
                                    if (initialMethod == null) initialMethod = findMethodInHierarchy(b.getClass(), "initial", Number.class);
                                    
                                    if (initialMethod != null) {
                                        try { initialMethod.setAccessible(true); } catch (Throwable ignored) {}
                                        Object returnedBuilder = initialMethod.invoke(b, range[2]);
                                        if (returnedBuilder != null) b = returnedBuilder;
                                    }
                                } catch (Exception e) {
                                    Bukkit.getLogger().warning("[MrUltimateLibrary] Failed to set slider default: " + e.getMessage());
                                }
                            }
                            inputs.add(b.getClass().getMethod("build").invoke(b));
                        } catch (Exception e) {
                            Bukkit.getLogger().log(Level.SEVERE, "[MrUltimateLibrary] Failed to build SLIDER element: " + el.id, e);
                        }
                        break;
                }
            }

            
            Method baseBuilderMethod = dialogBaseClass.getMethod("builder", componentClass);
            Object baseBuilder = baseBuilderMethod.invoke(null, titleComp);
            try { baseBuilder.getClass().getMethod("body", List.class).invoke(baseBuilder, bodies); }
            catch (Exception ignored) {}
            try { baseBuilder.getClass().getMethod("inputs", List.class).invoke(baseBuilder, inputs); }
            catch (Exception ignored) {}
            Object dialogBase = baseBuilder.getClass().getMethod("build").invoke(baseBuilder);

            
            String session    = UUID.randomUUID().toString().replace("-", "").substring(0, 10);
            String confirmKey = "mrlibform:ok_" + session;
            String cancelKey  = "mrlibform:no_" + session;

            
            Method btnMethod   = actionButtonClass.getMethod("builder", componentClass);
            Object confirmBtn  = buildButton(btnMethod, createComp, "<color:#AEFFC1><b>Confirm</b></color>", confirmKey,
                                             dialogActionClass, keyClass);
            Object cancelBtn   = buildButton(btnMethod, createComp, "<color:#FF8B8E><b>Cancel</b></color>",  cancelKey,
                                             dialogActionClass, keyClass);

            
            Method confirmTypeMethod = dialogTypeClass.getMethod("confirmation", actionButtonClass, actionButtonClass);
            final Object dialogType  = confirmTypeMethod.invoke(null, confirmBtn, cancelBtn);

            
            Object dialog = null;
            try {
                Method direct = dialogClass.getMethod("dialog", dialogBaseClass, dialogTypeClass);
                dialog = direct.invoke(null, dialogBase, dialogType);
            } catch (Exception ignored) {}

            if (dialog == null) {
                final Object finalBase = dialogBase;
                final Object finalType = dialogType;
                InvocationHandler handler = (proxy, method, args) -> {
                    if (method.getName().equals("accept")) {
                        Object b = args[0];
                        try {
                            Method baseM = findMethod(b.getClass(), dialogBaseClass, true);
                            if (baseM != null) {
                                try {
                                    Object b2 = baseM.invoke(b, finalBase);
                                    if (b2 == null) b2 = b;
                                    Method typeM = findMethod(b2.getClass(), dialogTypeClass, false);
                                    if (typeM != null) { typeM.invoke(b2, finalType); return null; }
                                } catch (Exception directEx) { /* fall through */ }
                            }
                            Method emptyM = b.getClass().getMethod("empty");
                            Object b3 = emptyM.invoke(b);
                            if (b3 == null) b3 = b;
                            Method baseM2 = findMethod(b3.getClass(), dialogBaseClass, true);
                            Object b4 = baseM2.invoke(b3, finalBase);
                            if (b4 == null) b4 = b3;
                            Method typeM2 = findMethod(b4.getClass(), dialogTypeClass, false);
                            typeM2.invoke(b4, finalType);
                        } catch (Exception ex) { ex.printStackTrace(); }
                    }
                    return null;
                };
                Object proxy = Proxy.newProxyInstance(loader, new Class[]{consumerClass}, handler);
                dialog = dialogClass.getMethod("create", consumerClass).invoke(null, proxy);
            }

            
            registerPlayerCallback(player.getUniqueId(), confirmKey, cancelKey,
                                   hasInputs, elements, onSubmit, onCancel);

            
            Method showDialog = null;
            for (Method m : player.getClass().getMethods()) {
                if (m.getName().equals("showDialog") && m.getParameterCount() == 1) {
                    showDialog = m; break;
                }
            }
            if (showDialog == null) throw new NoSuchMethodException("Player#showDialog not found");
            Object future = showDialog.invoke(player, dialog);

            
            if (future != null) {
                try {
                    InvocationHandler fh = (proxy, method, args) -> {
                        if (method.getName().equals("accept")) {
                            Object response = args[0];
                            UUID uuid = player.getUniqueId();
                            pendingConfirm.remove(uuid);
                            pendingCancel.remove(uuid);
                            inputSubmitHandlers.remove(confirmKey);
                            inputCancelHandlers.remove(cancelKey);
                            try {
                                boolean confirmed = (boolean) response.getClass()
                                        .getMethod("isConfirmed").invoke(response);
                                Bukkit.getScheduler().runTask(MrUltimateLibrary.getInstance(), () ->
                                        dispatchResult(uuid, confirmed, response, elements, onSubmit, onCancel));
                            } catch (Exception ex) { ex.printStackTrace(); }
                        }
                        return null;
                    };
                    Object fp = Proxy.newProxyInstance(loader, new Class[]{consumerClass}, fh);
                    future.getClass().getMethod("thenAccept", consumerClass).invoke(future, fp);
                } catch (Exception ignored) {}
            }

        } catch (Exception e) {
            Bukkit.getLogger().severe("[MrUltimateLibrary] Dialog open failed: " + e.getMessage());
            e.printStackTrace();
            new MrLibFormFallback(title, elements, onSubmit, onCancel).open(player);
        }
    }

    
    
    

    private static Object getInputValue(Object responseView, MrLibForm.FormElement el) {
        if (responseView == null || el == null || el.id == null) return null;
        try {
            switch (el.type) {
                case TEXT:
                case DROPDOWN:
                    try {
                        return responseView.getClass().getMethod("getText", String.class).invoke(responseView, el.id);
                    } catch (Exception ignored) {}
                    break;
                case CHECKBOX:
                    try {
                        return responseView.getClass().getMethod("getBoolean", String.class).invoke(responseView, el.id);
                    } catch (Exception ignored) {}
                    break;
                case SLIDER:
                    try {
                        return responseView.getClass().getMethod("getFloat", String.class).invoke(responseView, el.id);
                    } catch (Exception ignored) {}
                    break;
            }
            
            for (String mName : new String[]{"getText", "getBoolean", "getFloat"}) {
                try {
                    Object val = responseView.getClass().getMethod(mName, String.class).invoke(responseView, el.id);
                    if (val != null) return val;
                } catch (Exception ignored) {}
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    
    
    

    private static Object buildButton(Method btnMethod, java.util.function.Function<String, Object> createComp,
                                       String label, String keyStr,
                                       Class<?> dialogActionClass, Class<?> keyClass) throws Exception {
        Object labelComp = createComp.apply(label);
        Object builder   = btnMethod.invoke(null, labelComp);

        if (dialogActionClass != null && keyClass != null) {
            try {
                Object keyObj = buildKey(keyClass, keyStr);
                Method customClick = null;
                for (Method m : dialogActionClass.getMethods()) {
                    if (m.getName().equals("customClick") && m.getParameterCount() >= 1) {
                        Class<?> firstParam = m.getParameterTypes()[0];
                        if (firstParam.isAssignableFrom(keyClass)) {
                            customClick = m;
                            break;
                        }
                    }
                }
                if (customClick != null && keyObj != null) {
                    Object action = customClick.getParameterCount() == 1
                            ? customClick.invoke(null, keyObj)
                            : customClick.invoke(null, keyObj, null);

                    boolean actionSet = false;
                    for (Method m : builder.getClass().getMethods()) {
                        if (m.getName().equals("action") && m.getParameterCount() == 1) {
                            Object ret = m.invoke(builder, action);
                            if (ret != null && ret != builder) builder = ret;
                            actionSet = true;
                            break;
                        }
                    }
                    if (!actionSet) {
                        throw new NoSuchMethodException("Button builder 'action' method not found");
                    }
                } else {
                    throw new NoSuchMethodException("customClick method or Key object not found");
                }
            } catch (Exception e) {
                throw new RuntimeException("Failed to setup dialog click action: " + e.getMessage(), e);
            }
        } else {
            throw new ClassNotFoundException("DialogAction or Key class not found");
        }

        return builder.getClass().getMethod("build").invoke(builder);
    }

    private static Object buildKey(Class<?> keyClass, String full) {
        try {
            Method m = keyClass.getMethod("key", String.class);
            return m.invoke(null, full);
        } catch (Exception e1) {
            try {
                String[] p = full.split(":", 2);
                Method m = keyClass.getMethod("key", String.class, String.class);
                return m.invoke(null, p[0], p[1]);
            } catch (Exception e2) { return null; }
        }
    }

    
    
    

    private static void registerPlayerCallback(UUID uuid,
                                                String confirmKey, String cancelKey,
                                                boolean hasInputs,
                                                List<MrLibForm.FormElement> elements,
                                                Consumer<MrLibForm.FormResult> onSubmit,
                                                Runnable onCancel) {
        ensureEventListenerRegistered();

        pendingConfirm.put(uuid, responseView -> {
            pendingConfirm.remove(uuid);
            pendingCancel.remove(uuid);
            inputSubmitHandlers.remove(confirmKey);
            inputCancelHandlers.remove(cancelKey);
            
            MrLibForm.FormResult result = new MrLibForm.FormResult();
            if (hasInputs && responseView != null) {
                for (MrLibForm.FormElement el : elements) {
                    if (el.id != null) {
                        Object val = getInputValue(responseView, el);
                        result.put(el.id, val != null ? val : "");
                    }
                }
            }
            if (onSubmit != null) onSubmit.accept(result);
        });

        pendingCancel.put(uuid, () -> {
            pendingConfirm.remove(uuid);
            pendingCancel.remove(uuid);
            inputSubmitHandlers.remove(confirmKey);
            inputCancelHandlers.remove(cancelKey);
            if (onCancel != null) onCancel.run();
        });

        inputSubmitHandlers.put(confirmKey, responseView -> {
            Consumer<Object> cb = pendingConfirm.remove(uuid);
            pendingCancel.remove(uuid);
            if (cb != null) {
                Bukkit.getScheduler().runTask(MrUltimateLibrary.getInstance(), () -> cb.accept(responseView));
            }
        });
        inputCancelHandlers.put(cancelKey, () -> {
            pendingConfirm.remove(uuid);
            Runnable cb = pendingCancel.remove(uuid);
            if (cb != null) {
                Bukkit.getScheduler().runTask(MrUltimateLibrary.getInstance(), cb);
            }
        });
    }

    @SuppressWarnings("unchecked")
    private static void ensureEventListenerRegistered() {
        if (eventRegistered) return;
        eventRegistered = true;
        try {
            ClassLoader loader = Bukkit.class.getClassLoader();
            Class<? extends Event> eventClass = (Class<? extends Event>)
                    Class.forName("io.papermc.paper.event.player.PlayerCustomClickEvent", true, loader);

            Bukkit.getPluginManager().registerEvent(
                    eventClass,
                    new Listener() {},
                    EventPriority.NORMAL,
                    new EventExecutor() {
                        @Override
                        public void execute(Listener listener, Event event) {
                            try {
                                Object identifier = event.getClass().getMethod("getIdentifier").invoke(event);
                                String keyStr = identifier.toString();

                                Player player = null;
                                try {
                                    player = (Player) event.getClass().getMethod("getPlayer").invoke(event);
                                } catch (Exception ignored) {}
                                if (player == null) {
                                    Object conn = event.getClass().getMethod("getCommonConnection").invoke(event);
                                    player = (Player) conn.getClass().getMethod("getPlayer").invoke(conn);
                                }

                                Object responseView = null;
                                try {
                                    responseView = event.getClass().getMethod("getDialogResponseView").invoke(event);
                                } catch (Exception ignored) {}

                                if (inputSubmitHandlers.containsKey(keyStr)) {
                                    inputSubmitHandlers.remove(keyStr).accept(responseView);
                                } else if (inputCancelHandlers.containsKey(keyStr)) {
                                    inputCancelHandlers.remove(keyStr).run();
                                }
                            } catch (Exception ex) {
                                ex.printStackTrace();
                            }
                        }
                    },
                    MrUltimateLibrary.getInstance()
            );
        } catch (Exception e) {
            Bukkit.getLogger().warning("[MrUltimateLibrary] Could not register PlayerCustomClickEvent: " + e.getMessage());
            eventRegistered = false;
        }
    }

    private static Method findMethod(Class<?> cls, Class<?> targetType, boolean assignable) {
        for (Method m : cls.getMethods()) {
            if (m.getParameterCount() == 1) {
                Class<?> p = m.getParameterTypes()[0];
                if (assignable ? p.isAssignableFrom(targetType) : p.equals(targetType)) return m;
            }
        }
        return null;
    }

    private static void dispatchResult(UUID uuid, boolean confirmed, Object response,
                                        List<MrLibForm.FormElement> elements,
                                        Consumer<MrLibForm.FormResult> onSubmit,
                                        Runnable onCancel) {
        if (confirmed) {
            MrLibForm.FormResult result = new MrLibForm.FormResult();
            if (response != null) {
                for (MrLibForm.FormElement el : elements) {
                    if (el.id != null) {
                        Object val = getInputValue(response, el);
                        result.put(el.id, val != null ? val : "");
                    }
                }
            }
            if (onSubmit != null) onSubmit.accept(result);
        } else {
            if (onCancel != null) onCancel.run();
        }
    }

    private static String legacyToMiniMessage(String input) {
        if (input == null) return "";
        
        String s = input.replace("&", "§");
        
        
        
        java.util.regex.Pattern gradPattern = java.util.regex.Pattern.compile("\\{#([0-9A-Fa-f]{6})>\\}(.*?)\\{#([0-9A-Fa-f]{6})<\\}");
        java.util.regex.Matcher gradMatcher = gradPattern.matcher(s);
        StringBuffer sb = new StringBuffer();
        while (gradMatcher.find()) {
            String startColor = gradMatcher.group(1);
            String text = gradMatcher.group(2);
            String endColor = gradMatcher.group(3);
            gradMatcher.appendReplacement(sb, "<gradient:#" + startColor + ":#" + endColor + ">" + java.util.regex.Matcher.quoteReplacement(text) + "</gradient>");
        }
        gradMatcher.appendTail(sb);
        s = sb.toString();
        
        
        java.util.regex.Pattern hexPattern = java.util.regex.Pattern.compile("\\{#([0-9A-Fa-f]{6})\\}");
        java.util.regex.Matcher hexMatcher = hexPattern.matcher(s);
        sb = new StringBuffer();
        while (hexMatcher.find()) {
            String hex = hexMatcher.group(1);
            hexMatcher.appendReplacement(sb, "<color:#" + hex + ">");
        }
        hexMatcher.appendTail(sb);
        s = sb.toString();
        
        
        java.util.regex.Pattern anglePattern = java.util.regex.Pattern.compile("<#([0-9A-Fa-f]{6})>");
        java.util.regex.Matcher angleMatcher = anglePattern.matcher(s);
        sb = new StringBuffer();
        while (angleMatcher.find()) {
            String hex = angleMatcher.group(1);
            angleMatcher.appendReplacement(sb, "<color:#" + hex + ">");
        }
        angleMatcher.appendTail(sb);
        s = sb.toString();
        
        
        s = s.replace("§0", "<black>");
        s = s.replace("§1", "<dark_blue>");
        s = s.replace("§2", "<dark_green>");
        s = s.replace("§3", "<dark_aqua>");
        s = s.replace("§4", "<dark_red>");
        s = s.replace("§5", "<dark_purple>");
        s = s.replace("§6", "<gold>");
        s = s.replace("§7", "<gray>");
        s = s.replace("§8", "<dark_gray>");
        s = s.replace("§9", "<blue>");
        s = s.replace("§a", "<green>").replace("§A", "<green>");
        s = s.replace("§b", "<aqua>").replace("§B", "<aqua>");
        s = s.replace("§c", "<red>").replace("§C", "<red>");
        s = s.replace("§d", "<light_purple>").replace("§D", "<light_purple>");
        s = s.replace("§e", "<yellow>").replace("§E", "<yellow>");
        s = s.replace("§f", "<white>").replace("§F", "<white>");
        s = s.replace("§l", "<bold>").replace("§L", "<bold>");
        s = s.replace("§o", "<italic>").replace("§O", "<italic>");
        s = s.replace("§n", "<underlined>").replace("§N", "<underlined>");
        s = s.replace("§m", "<strikethrough>").replace("§M", "<strikethrough>");
        s = s.replace("§k", "<obfuscated>").replace("§K", "<obfuscated>");
        s = s.replace("§r", "<reset>").replace("§R", "<reset>");
        
        
        s = s.replace("§", "");
        
        return s;
    }
}
