package net.mcreator.phayriosisreborn.client;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen;
import net.minecraft.network.chat.Component;
import net.mcreator.phayriosisreborn.client.gui.PhayriosisWorldCreationScreen;
import net.mcreator.phayriosisreborn.config.PhayriosisPendingWorldSettings;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;

/**
 * SRP-inspired Create World integration for 1.20.1.
 *
 * SRP 1.12.2 owns one dedicated button, lays it out relative to a vanilla
 * Create World control, and updates it after GUI actions/page changes. 1.20.1
 * uses a TabNavigationBar instead of the old "More World Options" page, so
 * this class tracks that vanilla tab bar and changes the Phayriosis button
 * only after init / mouse / keyboard tab actions. No per-frame widget chasing.
 */
public final class PhayriosisCreateWorldClient {
    private static final Map<CreateWorldScreen, Button> PHAYRIOSIS_BUTTONS = new WeakHashMap<>();
    private static final Map<CreateWorldScreen, Object> TAB_BARS = new WeakHashMap<>();

    public PhayriosisCreateWorldClient() {}

    @SubscribeEvent
    public static void onScreenInit(ScreenEvent.Init.Post event) {
        Screen screen = event.getScreen();
        if (!(screen instanceof CreateWorldScreen createWorld)) return;

        AbstractWidget allowCheats = findAllowCheats(event.getListenersList());
        Object tabBar = findTabNavigationBar(event.getListenersList());
        if (tabBar != null) TAB_BARS.put(createWorld, tabBar);

        // Remove any previous Phayriosis button owned by this same screen before
        // laying it out again. This mirrors SRP's one-button lifecycle.
        Button old = PHAYRIOSIS_BUTTONS.remove(createWorld);
        if (old != null) event.getListenersList().remove(old);

        // The Game tab owns Allow Cheats. If it is not currently in the active
        // listener set, do not create a detached button on World/More.
        if (allowCheats == null) return;

        int x = allowCheats.m_252754_();
        int y = allowCheats.m_252907_() + allowCheats.m_93694_() + 4;
        int width = allowCheats.m_5711_();

        Button button = Button.m_253074_(Component.m_237113_("Phayriosis"), b ->
                Minecraft.m_91087_().m_91152_(new PhayriosisWorldCreationScreen(screen)))
                .m_252987_(x, y, width, 20).m_253136_();

        boolean gameTab = tabBar == null || isGameTabSelected(tabBar);
        button.f_93624_ = gameTab;
        button.f_93623_ = gameTab;
        PHAYRIOSIS_BUTTONS.put(createWorld, button);
        event.addListener(button);
    }

    @SubscribeEvent
    public static void onMouseButtonPost(ScreenEvent.MouseButtonPressed.Post event) {
        syncAfterTabAction(event.getScreen());
    }

    @SubscribeEvent
    public static void onKeyPressedPost(ScreenEvent.KeyPressed.Post event) {
        syncAfterTabAction(event.getScreen());
    }

    private static void syncAfterTabAction(Screen screen) {
        if (!(screen instanceof CreateWorldScreen createWorld)) return;
        Button button = PHAYRIOSIS_BUTTONS.get(createWorld);
        Object tabBar = TAB_BARS.get(createWorld);
        if (button == null || tabBar == null) return;
        boolean gameTab = isGameTabSelected(tabBar);
        button.f_93624_ = gameTab;
        button.f_93623_ = gameTab;
    }

    private static AbstractWidget findAllowCheats(List<GuiEventListener> listeners) {
        for (GuiEventListener listener : listeners) {
            if (!(listener instanceof AbstractWidget widget)) continue;
            Component message = widget.m_6035_();
            if (message == null) continue;
            String label = message.getString().toLowerCase(Locale.ROOT);
            if (label.contains("allow cheats") || label.contains("allow commands")) return widget;
        }
        return null;
    }

    private static Object findTabNavigationBar(List<GuiEventListener> listeners) {
        for (GuiEventListener listener : listeners) {
            if (listener != null &&
                    "net.minecraft.client.gui.components.tabs.TabNavigationBar".equals(listener.getClass().getName())) {
                return listener;
            }
        }
        return null;
    }

    /**
     * Avoids private-field access and mapping-sensitive Minecraft method names.
     * TabNavigationBar exposes its TabButtons publicly; we identify its selected
     * TabButton by public method shape, then inspect the selected tab's class.
     */
    @SuppressWarnings("unchecked")
    private static boolean isGameTabSelected(Object tabBar) {
        try {
            Method children = null;
            for (Method method : tabBar.getClass().getMethods()) {
                if (method.getParameterCount() == 0 && List.class.isAssignableFrom(method.getReturnType())) {
                    children = method;
                    break;
                }
            }
            if (children == null) return true;

            Object value = children.invoke(tabBar);
            if (!(value instanceof List<?> tabButtons)) return true;

            for (Object tabButton : tabButtons) {
                if (tabButton == null ||
                        !"net.minecraft.client.gui.components.TabButton".equals(tabButton.getClass().getName())) continue;

                Method selectedMethod = null;
                Method tabMethod = null;
                for (Method method : tabButton.getClass().getDeclaredMethods()) {
                    if (!Modifier.isPublic(method.getModifiers()) || method.getParameterCount() != 0) continue;
                    if (method.getReturnType() == boolean.class) selectedMethod = method;
                    else if ("net.minecraft.client.gui.components.tabs.Tab".equals(method.getReturnType().getName())) tabMethod = method;
                }
                if (selectedMethod == null || tabMethod == null) continue;
                if (!Boolean.TRUE.equals(selectedMethod.invoke(tabButton))) continue;

                Object tab = tabMethod.invoke(tabButton);
                return tab != null && "GameTab".equals(tab.getClass().getSimpleName());
            }
        } catch (ReflectiveOperationException | RuntimeException ignored) {
            // Fail open on the Game page rather than crashing Create World.
        }
        return true;
    }

    @SubscribeEvent
    public static void onOpening(ScreenEvent.Opening event) {
        Screen next = event.getNewScreen();
        if (next == null) return;
        String name = next.getClass().getSimpleName();
        if ("SelectWorldScreen".equals(name) || "TitleScreen".equals(name)) {
            PhayriosisPendingWorldSettings.cancelAll();
        }
    }
}
