package de.winniepat.citrus.gui;

import de.winniepat.citrus.draggui.HudConfig;
import icyllis.modernui.animation.*;
import icyllis.modernui.core.Context;
import icyllis.modernui.fragment.Fragment;
import icyllis.modernui.graphics.drawable.*;
import icyllis.modernui.mc.*;
import icyllis.modernui.util.DataSet;
import icyllis.modernui.view.*;
import icyllis.modernui.widget.*;
import net.minecraft.client.MinecraftClient;
import org.jetbrains.annotations.*;
import de.winniepat.citrus.cosmetics.capes.CapeHandler;

import java.util.*;

public class HudEditorFragment extends Fragment implements ScreenCallback {

    private FrameLayout mModuleContainer;
    private final Map<String, ModuleCard> mModuleCards = new LinkedHashMap<>();
    private final List<View> mStaggeredViews = new ArrayList<>();

    private ModuleCard mSelectedModule = null;
    private float mDragOffsetX, mDragOffsetY;

    private float mScaleFactor = 1.0f;
    private float mUiScale = HudConfig.INSTANCE.uiScale;

    @Override
    public View onCreateView(@NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable DataSet savedInstanceState) {
        Context context = requireContext();
        HudConfig.load();

        FrameLayout root = new FrameLayout(context);
        root.setLayoutParams(new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        ));

        root.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
            if (mScaleFactor == 1.0f && right > 0) {
                MinecraftClient mc = MinecraftClient.getInstance();
                int mcScaledWidth = mc.getWindow().getScaledWidth();
                int muiWidth = right - left;
                mScaleFactor = (float) muiWidth / (float) mcScaledWidth;

                repositionAllCards();
                updateAllCardSizes();
            }
        });

        root.setBackground(new ColorDrawable(0x88000000));

        mModuleContainer = new FrameLayout(context);
        mModuleContainer.setLayoutParams(new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        ));
        root.addView(mModuleContainer);

        createModuleCard("FPS", HudConfig.INSTANCE.fpsX, HudConfig.INSTANCE.fpsY, HudConfig.INSTANCE.fpsEnabled, HudConfig.INSTANCE.fpsColor);
        createModuleCard("Ping", HudConfig.INSTANCE.pingX, HudConfig.INSTANCE.pingY, HudConfig.INSTANCE.pingEnabled, HudConfig.INSTANCE.pingColor);
        createModuleCard("Server", HudConfig.INSTANCE.serverX, HudConfig.INSTANCE.serverY, HudConfig.INSTANCE.serverEnabled, HudConfig.INSTANCE.serverColor);
        createModuleCard("Keys", HudConfig.INSTANCE.keyX, HudConfig.INSTANCE.keyY, HudConfig.INSTANCE.keyEnabled, HudConfig.INSTANCE.keyColor);
        createModuleCard("Armor", HudConfig.INSTANCE.armorX, HudConfig.INSTANCE.armorY, HudConfig.INSTANCE.armorEnabled, HudConfig.INSTANCE.armorColor);
        createModuleCard("Potion", HudConfig.INSTANCE.potionX, HudConfig.INSTANCE.potionY, HudConfig.INSTANCE.potionEnabled, HudConfig.INSTANCE.potionColor);

        applyScaleToModules(mUiScale);

        LinearLayout mSidePanel = createSidePanel(context);
        root.addView(mSidePanel);

        TextView title = new TextView(context);
        title.setText("HUD Editor");
        title.setTextSize(24);
        title.setTextColor(0xFFFFFFFF);
        FrameLayout.LayoutParams titleLp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        titleLp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
        titleLp.setMargins(0, dp(20), 0, 0);
        title.setLayoutParams(titleLp);
        root.addView(title);
        mStaggeredViews.add(title);

        TextView instructions = new TextView(context);
        instructions.setText("Drag modules to reposition • Use panel on right to toggle & customize");
        instructions.setTextSize(12);
        instructions.setTextColor(0x88FFFFFF);
        FrameLayout.LayoutParams instrLp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        instrLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        instrLp.setMargins(0, 0, 0, dp(20));
        instructions.setLayoutParams(instrLp);
        root.addView(instructions);
        mStaggeredViews.add(instructions);

        animateEntrance();

        return root;
    }

    private void createModuleCard(String name, int x, int y, boolean enabled, int color) {
        Context context = requireContext();

        ModuleCard card = new ModuleCard(context, name, enabled, color);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        lp.leftMargin = mcToMui(x);
        lp.topMargin = mcToMui(y);
        card.setLayoutParams(lp);
        applyModuleCardSize(name, card);

        card.setOnTouchListener((v, event) -> {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN -> {
                    mSelectedModule = card;
                    mDragOffsetX = event.getX();
                    mDragOffsetY = event.getY();
                    card.setSelected(true);
                    animateSelect(card);
                    updateSidePanelForModule(card);
                    return true;
                }
                case MotionEvent.ACTION_MOVE -> {
                    if (mSelectedModule == card) {
                        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) card.getLayoutParams();
                        params.leftMargin = (int) (event.getRawX() - mDragOffsetX);
                        params.topMargin = (int) (event.getRawY() - mDragOffsetY);
                        card.setLayoutParams(params);
                        updateConfigPosition(name, params.leftMargin, params.topMargin);
                        return true;
                    }
                }
                case MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                    if (mSelectedModule == card) {
                        animateDeselect(card);
                        HudConfig.save();
                    }
                    return true;
                }
            }
            return false;
        });

        mModuleContainer.addView(card);
        mModuleCards.put(name, card);
        mStaggeredViews.add(card);
    }

    private void updateConfigPosition(String name, int x, int y) {
        int mcX = muiToMc(x);
        int mcY = muiToMc(y);
        switch (name) {
            case "FPS" -> { HudConfig.INSTANCE.fpsX = mcX; HudConfig.INSTANCE.fpsY = mcY; }
            case "Ping" -> { HudConfig.INSTANCE.pingX = mcX; HudConfig.INSTANCE.pingY = mcY; }
            case "Server" -> { HudConfig.INSTANCE.serverX = mcX; HudConfig.INSTANCE.serverY = mcY; }
            case "Keys" -> { HudConfig.INSTANCE.keyX = mcX; HudConfig.INSTANCE.keyY = mcY; }
            case "Armor" -> { HudConfig.INSTANCE.armorX = mcX; HudConfig.INSTANCE.armorY = mcY; }
            case "Potion" -> { HudConfig.INSTANCE.potionX = mcX; HudConfig.INSTANCE.potionY = mcY; }
        }
    }

    private LinearLayout createSidePanel(Context context) {
        LinearLayout panel = new LinearLayout(context);
        panel.setOrientation(LinearLayout.VERTICAL);
        panel.setPadding(dp(16), dp(16), dp(16), dp(16));

        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(dp(280), ViewGroup.LayoutParams.MATCH_PARENT);
        lp.gravity = Gravity.RIGHT;
        panel.setLayoutParams(lp);

        ShapeDrawable bg = new ShapeDrawable();
        bg.setShape(ShapeDrawable.RECTANGLE);
        bg.setColor(0xE6181818);
        bg.setCornerRadii(dp(16), 0, 0, dp(16));
        panel.setBackground(bg);

        TextView panelTitle = new TextView(context);
        panelTitle.setText("Module Settings");
        panelTitle.setTextSize(18);
        panelTitle.setTextColor(0xFFFFFFFF);
        panelTitle.setPadding(0, 0, 0, dp(16));
        panel.addView(panelTitle);

        ScrollView scroll = new ScrollView(context);
        scroll.setLayoutParams(new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        ));

        LinearLayout content = new LinearLayout(context);
        content.setOrientation(LinearLayout.VERTICAL);
        content.setId(View.generateViewId());

        for (String moduleName : new String[]{"FPS", "Ping", "Server", "Keys", "Armor", "Potion"}) {
            content.addView(createModuleSettingItem(context, moduleName));
        }

        View separator = new View(context);
        separator.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(1)));
        separator.setBackground(new ColorDrawable(0x33FFFFFF));
        LinearLayout.LayoutParams sepLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(1));
        sepLp.setMargins(0, dp(16), 0, dp(16));
        separator.setLayoutParams(sepLp);
        content.addView(separator);

        content.addView(createSliderSetting(context, "UI Scale", 50, 150, (int) (HudConfig.INSTANCE.uiScale * 100), value -> {
            float scale = value / 100f;
            applyScaleToModules(scale);
            HudConfig.save();
        }));

        Button resetBtn = new Button(context);
        resetBtn.setText("Reset Positions");
        LinearLayout.LayoutParams resetLp = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                dp(44)
        );

        LinearLayout list = new LinearLayout(context);
        list.setOrientation(LinearLayout.VERTICAL);

        for (String cape : CapeHandler.getAvailableCapes()) {

            Button btn = new Button(context);
            btn.setText((cape));

            btn.setOnClickListener(v -> {
                CapeHandler.setLocalPlayerCape(cape, false);
            });

            LinearLayout.LayoutParams blp = new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    dp(44)
            );
            blp.setMargins(0, dp(6), 0, 0);
            btn.setLayoutParams(blp);

            list.addView(btn);
        }

        resetLp.setMargins(0, dp(16), 0, 0);
        resetBtn.setLayoutParams(resetLp);
        resetBtn.setOnClickListener(v -> resetPositions());
        content.addView(resetBtn);

        scroll.addView(content);
        panel.addView(scroll);

        mStaggeredViews.add(panel);
        return panel;
    }

    private LinearLayout createModuleSettingItem(Context context, String moduleName) {
        LinearLayout item = new LinearLayout(context);
        item.setOrientation(LinearLayout.VERTICAL);
        item.setPadding(dp(12), dp(12), dp(12), dp(12));
        LinearLayout.LayoutParams itemLp = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        itemLp.setMargins(0, 0, 0, dp(8));
        item.setLayoutParams(itemLp);

        ShapeDrawable itemBg = new ShapeDrawable();
        itemBg.setShape(ShapeDrawable.RECTANGLE);
        itemBg.setColor(0x1AFFFFFF);
        itemBg.setCornerRadius(dp(8));
        item.setBackground(itemBg);

        LinearLayout header = new LinearLayout(context);
        header.setOrientation(LinearLayout.HORIZONTAL);
        header.setGravity(Gravity.CENTER_VERTICAL);

        TextView nameTv = new TextView(context);
        nameTv.setText(moduleName);
        nameTv.setTextSize(14);
        nameTv.setTextColor(0xFFFFFFFF);
        nameTv.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
        header.addView(nameTv);

        CheckBox toggle = new CheckBox(context);
        toggle.setChecked(HudConfig.INSTANCE.isModuleEnabled(moduleName));
        toggle.setOnCheckedChangeListener((buttonView, isChecked) -> {
            setModuleEnabled(moduleName, isChecked);
            ModuleCard card = mModuleCards.get(moduleName);
            if (card != null) {
                card.setModuleEnabled(isChecked);
                animateVisibility(card, isChecked);
            }
        });
        header.addView(toggle);

        item.addView(header);

        TextView colorLabel = new TextView(context);
        colorLabel.setText("Color");
        colorLabel.setTextSize(11);
        colorLabel.setTextColor(0x88FFFFFF);
        colorLabel.setPadding(0, dp(8), 0, dp(4));
        item.addView(colorLabel);

        final int initialColor = getModuleColor(moduleName) | 0xFF000000;

        LinearLayout colorRow = new LinearLayout(context);
        colorRow.setOrientation(LinearLayout.HORIZONTAL);
        colorRow.setGravity(Gravity.CENTER_VERTICAL);

        SeekBar colorSlider = new SeekBar(context);
        colorSlider.setMax(360);
        colorSlider.setProgress(getHueFromColor(initialColor));
        colorSlider.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));

        Button minus = new Button(context);
        minus.setText("-");
        minus.setOnClickListener(v -> {
            int step = Math.max(0, colorSlider.getProgress() - 5);
            colorSlider.setProgress(step);
        });


        Button plus = new Button(context);
        plus.setText("+");
        plus.setOnClickListener(v -> {
            int step = Math.min(360, colorSlider.getProgress() + 5);
            colorSlider.setProgress(step);
        });

        View colorPreview = new View(context);
        LinearLayout.LayoutParams previewLp = new LinearLayout.LayoutParams(dp(28), dp(20));
        previewLp.setMargins(dp(8), 0, 0, 0);
        colorPreview.setLayoutParams(previewLp);
        colorPreview.setBackground(new ColorDrawable(initialColor));

        colorRow.addView(minus);
        colorRow.addView(colorSlider);
        colorRow.addView(plus);
        colorRow.addView(colorPreview);
        item.addView(colorRow);

        LinearLayout hexRow = new LinearLayout(context);
        hexRow.setOrientation(LinearLayout.HORIZONTAL);
        hexRow.setGravity(Gravity.CENTER_VERTICAL);
        hexRow.setPadding(0, dp(6), 0, 0);

        EditText hexInput = new EditText(context);
        hexInput.setSingleLine(true);
        hexInput.setText(formatColor(initialColor));
        LinearLayout.LayoutParams hexLp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
        hexLp.setMargins(0, 0, dp(8), 0);
        hexInput.setLayoutParams(hexLp);

        Button resetColor = new Button(context);
        resetColor.setText("White");
        resetColor.setOnClickListener(v -> {
            int white = 0xFFFFFFFF;
            setModuleColor(moduleName, white);
            colorSlider.setProgress(getHueFromColor(white));
            ModuleCard card = mModuleCards.get(moduleName);
            if (card != null) {
                card.updateColor(white);
            }
            colorPreview.setBackground(new ColorDrawable(white));
            hexInput.setText(formatColor(white));
            HudConfig.save();
        });

        hexRow.addView(hexInput);
        hexRow.addView(resetColor);
        item.addView(hexRow);

        colorSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                int color = java.awt.Color.HSBtoRGB(progress / 360f, 0.8f, 1f) | 0xFF000000;
                setModuleColor(moduleName, color);
                ModuleCard card = mModuleCards.get(moduleName);
                if (card != null) {
                    card.updateColor(color);
                }
                colorPreview.setBackground(new ColorDrawable(color));
                hexInput.setText(formatColor(color));
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                HudConfig.save();
            }
        });

        return item;
    }

    private LinearLayout createSliderSetting(Context context, String label, int min, int max, int defaultVal, SliderCallback callback) {
        LinearLayout container = new LinearLayout(context);
        container.setOrientation(LinearLayout.VERTICAL);
        container.setPadding(0, dp(8), 0, dp(8));

        TextView labelTv = new TextView(context);
        labelTv.setText(label);
        labelTv.setTextSize(14);
        labelTv.setTextColor(0xFFFFFFFF);
        container.addView(labelTv);

        LinearLayout sliderRow = new LinearLayout(context);
        sliderRow.setOrientation(LinearLayout.HORIZONTAL);
        sliderRow.setGravity(Gravity.CENTER_VERTICAL);

        SeekBar slider = new SeekBar(context);
        slider.setMax(max - min);
        slider.setProgress(defaultVal - min);
        slider.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));

        TextView valueTv = new TextView(context);
        valueTv.setText(defaultVal + "%");
        valueTv.setTextSize(12);
        valueTv.setTextColor(0xAAFFFFFF);
        valueTv.setMinWidth(dp(40));
        valueTv.setGravity(Gravity.RIGHT);

        slider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                int value = progress + min;
                valueTv.setText(value + "%");
                callback.onValueChanged(value);
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                HudConfig.save();
            }
        });

        sliderRow.addView(slider);
        sliderRow.addView(valueTv);
        container.addView(sliderRow);

        return container;
    }

    private void resetPositions() {
        HudConfig.INSTANCE.fpsX = 10; HudConfig.INSTANCE.fpsY = 10;
        HudConfig.INSTANCE.pingX = 10; HudConfig.INSTANCE.pingY = 25;
        HudConfig.INSTANCE.serverX = 10; HudConfig.INSTANCE.serverY = 40;
        HudConfig.INSTANCE.keyX = 10; HudConfig.INSTANCE.keyY = 60;
        HudConfig.INSTANCE.armorX = 10; HudConfig.INSTANCE.armorY = 120;
        HudConfig.INSTANCE.potionX = 10; HudConfig.INSTANCE.potionY = 180;

        for (Map.Entry<String, ModuleCard> entry : mModuleCards.entrySet()) {
            ModuleCard card = entry.getValue();
            int targetX = 10, targetY = 10;
            switch (entry.getKey()) {
                case "Ping" -> targetY = 25;
                case "Server" -> targetY = 40;
                case "Keys" -> targetY = 60;
                case "Armor" -> targetY = 120;
                case "Potion" -> targetY = 180;
            }

            final int finalTargetX = mcToMui(targetX);
            final int finalTargetY = mcToMui(targetY);

            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) card.getLayoutParams();
            final int startX = lp.leftMargin;
            final int startY = lp.topMargin;

            ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
            animator.setDuration(300);
            animator.setInterpolator(MotionEasingUtils.MOTION_EASING_EMPHASIZED);
            animator.addUpdateListener(animation -> {
                float fraction = (float) animation.getAnimatedValue();
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) card.getLayoutParams();
                params.leftMargin = (int) (startX + (finalTargetX - startX) * fraction);
                params.topMargin = (int) (startY + (finalTargetY - startY) * fraction);
                card.setLayoutParams(params);
            });
            animator.start();
        }
        HudConfig.save();
    }

    private void animateSelect(View v) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(v, View.SCALE_X, 1f, 1.05f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(v, View.SCALE_Y, 1f, 1.05f);
        AnimatorSet set = new AnimatorSet();
        set.playTogether(scaleX, scaleY);
        set.setDuration(150);
        set.setInterpolator(MotionEasingUtils.MOTION_EASING_EMPHASIZED);
        set.start();
    }

    private void animateDeselect(View v) {
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(v, View.SCALE_X, 1.05f, 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(v, View.SCALE_Y, 1.05f, 1f);
        AnimatorSet set = new AnimatorSet();
        set.playTogether(scaleX, scaleY);
        set.setDuration(150);
        set.setInterpolator(MotionEasingUtils.MOTION_EASING_EMPHASIZED);
        set.start();
    }

    private void animateVisibility(View v, boolean visible) {
        float targetAlpha = visible ? 1f : 0.3f;
        ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, v.getAlpha(), targetAlpha);
        alpha.setDuration(200);
        alpha.setInterpolator(MotionEasingUtils.LINEAR_OUT_SLOW_IN);
        alpha.start();
    }

    private void animateEntrance() {
        long delay = 0;
        for (View v : mStaggeredViews) {
            v.setAlpha(0);
            v.setTranslationY(dp(30));

            ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
            ObjectAnimator trans = ObjectAnimator.ofFloat(v, View.TRANSLATION_Y, dp(30), 0f);

            AnimatorSet set = new AnimatorSet();
            set.playTogether(alpha, trans);
            set.setDuration(400);
            set.setStartDelay(delay);
            set.setInterpolator(MotionEasingUtils.MOTION_EASING_EMPHASIZED_DECELERATE);
            set.start();

            delay += 50;
        }
    }

    private void updateSidePanelForModule(ModuleCard card) {

    }

    private void setModuleEnabled(String name, boolean enabled) {
        switch (name) {
            case "FPS" -> HudConfig.INSTANCE.fpsEnabled = enabled;
            case "Ping" -> HudConfig.INSTANCE.pingEnabled = enabled;
            case "Server" -> HudConfig.INSTANCE.serverEnabled = enabled;
            case "Keys" -> HudConfig.INSTANCE.keyEnabled = enabled;
            case "Armor" -> HudConfig.INSTANCE.armorEnabled = enabled;
            case "Potion" -> HudConfig.INSTANCE.potionEnabled = enabled;
        }
        HudConfig.save();
    }

    private void setModuleColor(String name, int color) {
        switch (name) {
            case "FPS" -> HudConfig.INSTANCE.fpsColor = color;
            case "Ping" -> HudConfig.INSTANCE.pingColor = color;
            case "Server" -> HudConfig.INSTANCE.serverColor = color;
            case "Keys" -> HudConfig.INSTANCE.keyColor = color;
            case "Armor" -> HudConfig.INSTANCE.armorColor = color;
            case "Potion" -> HudConfig.INSTANCE.potionColor = color;
        }
    }

    private int getModuleColor(String name) {
        return switch (name) {
            case "FPS" -> HudConfig.INSTANCE.fpsColor;
            case "Ping" -> HudConfig.INSTANCE.pingColor;
            case "Server" -> HudConfig.INSTANCE.serverColor;
            case "Keys" -> HudConfig.INSTANCE.keyColor;
            case "Armor" -> HudConfig.INSTANCE.armorColor;
            case "Potion" -> HudConfig.INSTANCE.potionColor;
            default -> 0xFFFFFFFF;
        };
    }

    private int getHueFromColor(int color) {
        float[] hsb = java.awt.Color.RGBtoHSB((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF, null);
        return (int) (hsb[0] * 360);
    }

    private void repositionAllCards() {
        for (Map.Entry<String, ModuleCard> entry : mModuleCards.entrySet()) {
            ModuleCard card = entry.getValue();
            int mcX = 0, mcY = 0;
            switch (entry.getKey()) {
                case "FPS" -> { mcX = HudConfig.INSTANCE.fpsX; mcY = HudConfig.INSTANCE.fpsY; }
                case "Ping" -> { mcX = HudConfig.INSTANCE.pingX; mcY = HudConfig.INSTANCE.pingY; }
                case "Server" -> { mcX = HudConfig.INSTANCE.serverX; mcY = HudConfig.INSTANCE.serverY; }
                case "Keys" -> { mcX = HudConfig.INSTANCE.keyX; mcY = HudConfig.INSTANCE.keyY; }
                case "Armor" -> { mcX = HudConfig.INSTANCE.armorX; mcY = HudConfig.INSTANCE.armorY; }
                case "Potion" -> { mcX = HudConfig.INSTANCE.potionX; mcY = HudConfig.INSTANCE.potionY; }
            }
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) card.getLayoutParams();
            lp.leftMargin = mcToMui(mcX);
            lp.topMargin = mcToMui(mcY);
            card.setLayoutParams(lp);
        }
    }

    private void updateAllCardSizes() {
        for (Map.Entry<String, ModuleCard> entry : mModuleCards.entrySet()) {
            applyModuleCardSize(entry.getKey(), entry.getValue());
        }
    }

    private void applyModuleCardSize(String name, ModuleCard card) {
        MinecraftClient mc = MinecraftClient.getInstance();
        int width = mcToMui(80);
        int height = mcToMui(mc.textRenderer.fontHeight + 4);

        switch (name) {
            case "Keys" -> {
                width = mcToMui(64);
                height = mcToMui(86);
            }
            case "Armor" -> {
                int armorCount = 0;
                if (mc.player != null) {
                    for (var stack : mc.player.getArmorItems()) {
                        if (!stack.isEmpty()) armorCount++;
                    }
                }
                int rows = Math.max(1, armorCount);
                width = mcToMui(50);
                height = mcToMui(18 * rows);
            }
            case "Potion" -> {
                int lines =  Math.max(1, mc.player != null ? mc.player.getStatusEffects().size() : 1);
                width = mcToMui(100);
                height = mcToMui(12 * lines);
            }
            default -> {
                String text = card.getCurrentDisplayText();
                width = mcToMui(mc.textRenderer.getWidth(text) + 6);
                height = mcToMui(mc.textRenderer.fontHeight + 4);
            }
        }

        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) card.getLayoutParams();
        if (lp != null) {
            lp.width = Math.max(width, dp(40));
            lp.height = Math.max(height, dp(20));
            card.setLayoutParams(lp);
        }
    }

    private int mcToMui(int mcCoord) {
        return (int) (mcCoord * mScaleFactor * mUiScale);
    }

    private int muiToMc(int muiCoord) {
        return (int) (muiCoord / (mScaleFactor * mUiScale));
    }

    private int dp(int value) {
        return (int) (value * getContext().getResources().getDisplayMetrics().density);
    }

    @Override
    public void onPause() {
        super.onPause();
        HudConfig.save();
    }

    @Override
    public boolean isPauseScreen() {
        return false;
    }

    @Override
    public boolean shouldBlurBackground() {
        return false;
    }

    @Override
    public boolean hasDefaultBackground() {
        return false;
    }

    private interface SliderCallback {
        void onValueChanged(int value);
    }

    private void applyScaleToModules(float scale) {
        mUiScale = scale;
        HudConfig.INSTANCE.uiScale = scale;
        repositionAllCards();
        updateAllCardSizes();
    }

    private String formatColor(int color) {
        return String.format(Locale.ROOT, "#%06X", color & 0xFFFFFF);
    }

    private int parseColorInput(String input, int fallback) {
        if (input == null) return fallback;
        String cleaned = input.trim();
        if (cleaned.startsWith("#")) cleaned = cleaned.substring(1);
        if (cleaned.length() == 6) {
            try {
                return (int) (0xFF000000L | Long.parseLong(cleaned, 16));
            } catch (NumberFormatException ignored) {
            }
        }
        return fallback;
    }

    private static class ModuleCard extends FrameLayout {
        private final String mName;
        private final TextView mLabel;
        private final ShapeDrawable mBackground;
        private int mColor;
        private boolean mModuleEnabled;

        public ModuleCard(Context context, String name, boolean enabled, int color) {
            super(context);
            mName = name;
            mColor = color;
            mModuleEnabled = enabled;

            setPadding(dp(12), dp(8), dp(12), dp(8));
            setMinimumWidth(dp(80));

            mBackground = new ShapeDrawable();
            mBackground.setShape(ShapeDrawable.RECTANGLE);
            mBackground.setCornerRadius(dp(8));
            updateBackgroundColor(false);
            setBackground(mBackground);

            mLabel = new TextView(context);
            mLabel.setText(getDisplayText());
            mLabel.setTextSize(14);
            mLabel.setTextColor(color);
            mLabel.setGravity(Gravity.CENTER);
            addView(mLabel);

            setAlpha(enabled ? 1f : 0.3f);

            setOnHoverListener((v, event) -> {
                if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
                    updateBackgroundColor(true);
                } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
                    if (!isSelected()) {
                        updateBackgroundColor(false);
                    }
                }
                return false;
            });
        }

        public String getCurrentDisplayText() {
            return getDisplayText();
        }

        private String getDisplayText() {
            MinecraftClient client = MinecraftClient.getInstance();
            return switch (mName) {
                case "FPS" -> client.getCurrentFps() + " FPS";
                case "Ping" -> {
                    int ping = 0;
                    if (client.getNetworkHandler() != null && client.player != null) {
                        var entry = client.getNetworkHandler().getPlayerListEntry(client.player.getUuid());
                        if (entry != null) ping = entry.getLatency();
                    }
                    yield ping + "ms";
                }
                case "Server" -> client.getCurrentServerEntry() != null ?
                        client.getCurrentServerEntry().address : "Singleplayer";
                case "Keys" -> "[WASD]";
                case "Armor" -> "[Armor]";
                case "Potion" -> "[Potions]";
                default -> mName;
            };
        }

        public void updateColor(int color) {
            mColor = color;
            mLabel.setTextColor(color);
        }

        public void setModuleEnabled(boolean enabled) {
            mModuleEnabled = enabled;
        }

        private void updateBackgroundColor(boolean hovered) {
            if (hovered || isSelected()) {
                mBackground.setColor(0x44FFFFFF);
            } else {
                mBackground.setColor(0x22FFFFFF);
            }
            invalidate();
        }

        @Override
        public void setSelected(boolean selected) {
            super.setSelected(selected);
            updateBackgroundColor(selected);
        }
    }
}
