package com.tiedup.remake.client.gui.widgets;

import static com.tiedup.remake.client.gui.util.GuiLayoutConstants.*;

import com.google.common.collect.ImmutableList;
import com.tiedup.remake.client.gui.util.GuiColors;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.narration.NarratableEntry;
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.joml.Quaternionf;

/**
 * List Entry for SlaveManagementScreen.
 * Displays slave info, preview, status icons, and action buttons.
 *
 * Uses ContainerObjectSelectionList.Entry for Minecraft's built-in list scrolling.
 */
@OnlyIn(Dist.CLIENT)
public class SlaveEntryWidget
    extends ContainerObjectSelectionList.Entry<SlaveEntryWidget>
{

    private final IBondageState slave;
    private final Minecraft mc;

    // Actions
    private final Consumer<IBondageState> onAdjust;
    private final Consumer<IBondageState> onShock;
    private final Consumer<IBondageState> onLocate;
    private final Consumer<IBondageState> onFree;

    // Layout constants - 2-column grid layout for buttons
    private static final int BUTTON_W = 42;
    private static final int BUTTON_H = 16;
    private static final int BUTTON_GAP = 3;
    private static final int BUTTON_COL_GAP = 4;
    private static final int BUTTON_GRID_W = BUTTON_W * 2 + BUTTON_COL_GAP;

    // Preview size
    private static final int PREVIEW_SIZE = PREVIEW_WIDTH_S; // 50px

    // Status icon colors (use centralized GuiColors)
    private static final int COLOR_BOUND = GuiColors.TYPE_BIND;
    private static final int COLOR_GAGGED = GuiColors.TYPE_GAG;
    private static final int COLOR_BLIND = GuiColors.TYPE_BLINDFOLD;
    private static final int COLOR_DEAF = GuiColors.TYPE_EARPLUGS;
    private static final int COLOR_COLLAR = GuiColors.TYPE_COLLAR;
    private static final int COLOR_MITTENS = GuiColors.TYPE_MITTENS;

    // Cached button info for click handling
    private List<ButtonInfo> cachedButtons;
    private int lastRenderLeft, lastRenderTop, lastRenderWidth;

    public SlaveEntryWidget(
        IBondageState slave,
        Consumer<IBondageState> onAdjust,
        Consumer<IBondageState> onShock,
        Consumer<IBondageState> onLocate,
        Consumer<IBondageState> onFree
    ) {
        this.slave = slave;
        this.mc = Minecraft.getInstance();
        this.onAdjust = onAdjust;
        this.onShock = onShock;
        this.onLocate = onLocate;
        this.onFree = onFree;
    }

    @Override
    public void render(
        GuiGraphics graphics,
        int index,
        int top,
        int left,
        int width,
        int height,
        int mouseX,
        int mouseY,
        boolean hovering,
        float partialTick
    ) {
        // Cache render positions for click handling
        this.lastRenderLeft = left;
        this.lastRenderTop = top;
        this.lastRenderWidth = width;

        Font font = mc.font;

        // Background
        int bgColor = hovering ? GuiColors.BG_LIGHT : GuiColors.BG_DARK;
        graphics.fill(left, top, left + width, top + height, bgColor);

        // Border
        int borderColor = GuiColors.BORDER_LIGHT;
        graphics.fill(left, top, left + width, top + 1, borderColor);
        graphics.fill(
            left,
            top + height - 1,
            left + width,
            top + height,
            borderColor
        );
        graphics.fill(left, top, left + 1, top + height, borderColor);
        graphics.fill(
            left + width - 1,
            top,
            left + width,
            top + height,
            borderColor
        );

        // === LEFT SECTION: Entity Preview ===
        renderEntityPreview(graphics, font, left, top, height);

        // === MIDDLE SECTION: Info ===
        int infoX = left + PREVIEW_SIZE + MARGIN_M;
        int infoWidth = width - PREVIEW_SIZE - BUTTON_GRID_W - MARGIN_M * 3;
        renderInfo(graphics, font, infoX, top, infoWidth, height);

        // === RIGHT SECTION: Action Buttons (2-column grid) ===
        renderButtons(graphics, font, mouseX, mouseY, left, top, width);
    }

    private void renderEntityPreview(
        GuiGraphics graphics,
        Font font,
        int left,
        int top,
        int height
    ) {
        LivingEntity entity = slave.asLivingEntity();
        if (entity == null) {
            // Fallback placeholder
            graphics.fill(
                left + MARGIN_S,
                top + MARGIN_S,
                left + MARGIN_S + PREVIEW_SIZE,
                top + height - MARGIN_S,
                GuiColors.BG_MEDIUM
            );
            graphics.drawCenteredString(
                font,
                "?",
                left + MARGIN_S + PREVIEW_SIZE / 2,
                top + height / 2 - 4,
                GuiColors.TEXT_GRAY
            );
            return;
        }

        int previewX = left + MARGIN_S + PREVIEW_SIZE / 2;
        int previewY = top + height - MARGIN_XS;
        int scale = 26;

        try {
            // Save original entity rotations
            float origBodyRot = entity.yBodyRot;
            float origHeadRot = entity.yHeadRot;
            float origYRot = entity.getYRot();
            float origXRot = entity.getXRot();
            float origYHeadRotO = entity.yHeadRotO;
            float origYBodyRotO = entity.yBodyRotO;
            float origXRotO = entity.xRotO;

            // Set neutral pose
            entity.yBodyRot = 180f;
            entity.yHeadRot = 180f;
            entity.setYRot(180f);
            entity.setXRot(0f);
            entity.yHeadRotO = entity.yHeadRot;
            entity.yBodyRotO = entity.yBodyRot;
            entity.xRotO = entity.getXRot();

            // Render entity
            Quaternionf poseRotation = new Quaternionf()
                .rotationX((float) Math.PI)
                .rotateY((float) Math.PI);
            Quaternionf cameraRotation = new Quaternionf();
            InventoryScreen.renderEntityInInventory(
                graphics,
                previewX,
                previewY,
                scale,
                poseRotation,
                cameraRotation,
                entity
            );

            // Restore rotations
            entity.yBodyRot = origBodyRot;
            entity.yHeadRot = origHeadRot;
            entity.setYRot(origYRot);
            entity.setXRot(origXRot);
            entity.yHeadRotO = origYHeadRotO;
            entity.yBodyRotO = origYBodyRotO;
            entity.xRotO = origXRotO;
        } catch (Exception e) {
            com.tiedup.remake.core.TiedUpMod.LOGGER.debug(
                "[SlaveEntryWidget] Failed to render entity preview",
                e
            );
            graphics.fill(
                left + MARGIN_S,
                top + MARGIN_S,
                left + MARGIN_S + PREVIEW_SIZE,
                top + ENTRY_HEIGHT - MARGIN_S,
                GuiColors.BG_MEDIUM
            );
        }
    }

    private void renderInfo(
        GuiGraphics graphics,
        Font font,
        int infoX,
        int top,
        int infoWidth,
        int height
    ) {
        LivingEntity entity = slave.asLivingEntity();

        // === Row 1: Name ===
        int row1Y = top + MARGIN_S;
        String name = slave.getKidnappedName();
        if (font.width(name) > infoWidth) {
            name =
                font.plainSubstrByWidth(name, infoWidth - font.width("...")) +
                "...";
        }
        graphics.drawString(font, name, infoX, row1Y, GuiColors.TEXT_WHITE);

        // === Row 2: Collar nickname (if present) ===
        int row2Y = row1Y + LINE_HEIGHT + 2;
        if (slave.hasNamedCollar()) {
            String collarName = slave.getNameFromCollar();
            if (!collarName.isEmpty()) {
                String displayName = "\"" + collarName + "\"";
                if (font.width(displayName) > infoWidth) {
                    displayName =
                        font.plainSubstrByWidth(
                            displayName,
                            infoWidth - font.width("...")
                        ) +
                        "...";
                }
                graphics.drawString(
                    font,
                    displayName,
                    infoX,
                    row2Y,
                    GuiColors.ACCENT_TAN
                );
            }
        }

        // === Row 3: Status icons ===
        int row3Y = row2Y + LINE_HEIGHT + 4;
        List<StatusIcon> statuses = buildStatusList();
        int iconX = infoX;

        for (StatusIcon status : statuses) {
            // Icon background
            graphics.fill(
                iconX,
                row3Y,
                iconX + STATUS_ICON_SIZE,
                row3Y + STATUS_ICON_SIZE,
                status.color
            );
            graphics.fill(
                iconX,
                row3Y,
                iconX + STATUS_ICON_SIZE,
                row3Y + 1,
                GuiColors.darken(status.color, 0.3f)
            );

            // Letter
            graphics.drawCenteredString(
                font,
                status.letter,
                iconX + STATUS_ICON_SIZE / 2,
                row3Y + 3,
                GuiColors.TEXT_WHITE
            );

            iconX += STATUS_ICON_SIZE + STATUS_ICON_SPACING;
        }

        // If no statuses, show "(no restraints)"
        if (statuses.isEmpty()) {
            graphics.drawString(
                font,
                "(no restraints)",
                infoX,
                row3Y + 2,
                GuiColors.TEXT_DISABLED
            );
        }

        // Distance (right after status icons, only if GPS collar)
        if (hasGPSCollar() && mc.player != null && entity != null) {
            double dist = mc.player.distanceTo(entity);
            String distText = String.format("%.0fm", dist);
            int distX = iconX + MARGIN_S;
            graphics.drawString(
                font,
                distText,
                distX,
                row3Y + 2,
                GuiColors.TEXT_GRAY
            );
        }

        // === Row 4: Health bar ===
        if (entity != null) {
            int row4Y = row3Y + STATUS_ICON_SIZE + 4;
            float healthRatio = entity.getHealth() / entity.getMaxHealth();
            int barWidth = 50;

            // Bar background
            graphics.fill(
                infoX,
                row4Y,
                infoX + barWidth,
                row4Y + 4,
                GuiColors.BG_DARK
            );

            // Bar fill
            int healthColor =
                healthRatio > 0.5f
                    ? GuiColors.SUCCESS
                    : (healthRatio > 0.25f
                          ? GuiColors.WARNING
                          : GuiColors.ERROR);
            graphics.fill(
                infoX,
                row4Y,
                infoX + (int) (barWidth * healthRatio),
                row4Y + 4,
                healthColor
            );

            // Health percentage
            String healthText = String.format("%.0f%%", healthRatio * 100);
            graphics.drawString(
                font,
                healthText,
                infoX + barWidth + MARGIN_S,
                row4Y - 2,
                GuiColors.TEXT_GRAY
            );

            // GPS zone status (right of health)
            if (hasGPSCollar()) {
                ItemStack collarStack = slave.getEquipment(BodyRegionV2.NECK);
                if (CollarHelper.hasGPS(collarStack)) {
                    boolean inSafeZone = isInAnySafeZone(
                        collarStack,
                        entity
                    );
                    String gpsText = inSafeZone ? "Safe" : "Outside!";
                    int gpsColor = inSafeZone
                        ? GuiColors.SUCCESS
                        : GuiColors.ERROR;
                    int gpsX =
                        infoX +
                        barWidth +
                        MARGIN_S +
                        font.width(healthText) +
                        MARGIN_M;
                    graphics.drawString(
                        font,
                        gpsText,
                        gpsX,
                        row4Y - 2,
                        gpsColor
                    );
                }
            }
        }
    }

    private record ButtonInfo(
        String text,
        int color,
        Runnable action,
        int x,
        int y
    ) {}

    private List<ButtonInfo> getVisibleButtons(int left, int top, int width) {
        List<ButtonInfo> buttons = new ArrayList<>();
        int gridX = left + width - BUTTON_GRID_W - MARGIN_S;
        int baseY = top + MARGIN_S;

        int index = 0;
        if (slave.isGagged() || slave.isBlindfolded()) {
            int col = index % 2;
            int row = index / 2;
            buttons.add(
                new ButtonInfo(
                    "Adjust",
                    GuiColors.INFO,
                    () -> {
                        if (onAdjust != null) onAdjust.accept(slave);
                    },
                    gridX + col * (BUTTON_W + BUTTON_COL_GAP),
                    baseY + row * (BUTTON_H + BUTTON_GAP)
                )
            );
            index++;
        }
        if (hasShockCollar()) {
            int col = index % 2;
            int row = index / 2;
            buttons.add(
                new ButtonInfo(
                    "Shock",
                    GuiColors.WARNING,
                    () -> {
                        if (onShock != null) onShock.accept(slave);
                    },
                    gridX + col * (BUTTON_W + BUTTON_COL_GAP),
                    baseY + row * (BUTTON_H + BUTTON_GAP)
                )
            );
            index++;
        }
        if (hasGPSCollar()) {
            int col = index % 2;
            int row = index / 2;
            buttons.add(
                new ButtonInfo(
                    "Locate",
                    GuiColors.SUCCESS,
                    () -> {
                        if (onLocate != null) onLocate.accept(slave);
                    },
                    gridX + col * (BUTTON_W + BUTTON_COL_GAP),
                    baseY + row * (BUTTON_H + BUTTON_GAP)
                )
            );
            index++;
        }
        if (slave.isCaptive()) {
            int col = index % 2;
            int row = index / 2;
            buttons.add(
                new ButtonInfo(
                    "Free",
                    GuiColors.ERROR,
                    () -> {
                        if (onFree != null) onFree.accept(slave);
                    },
                    gridX + col * (BUTTON_W + BUTTON_COL_GAP),
                    baseY + row * (BUTTON_H + BUTTON_GAP)
                )
            );
        }

        return buttons;
    }

    private void renderButtons(
        GuiGraphics graphics,
        Font font,
        int mouseX,
        int mouseY,
        int left,
        int top,
        int width
    ) {
        cachedButtons = getVisibleButtons(left, top, width);
        if (cachedButtons.isEmpty()) return;

        for (ButtonInfo btn : cachedButtons) {
            boolean hovered =
                mouseX >= btn.x &&
                mouseX < btn.x + BUTTON_W &&
                mouseY >= btn.y &&
                mouseY < btn.y + BUTTON_H;

            int bgColor = hovered
                ? GuiColors.lighten(btn.color, 0.2f)
                : btn.color;
            graphics.fill(
                btn.x,
                btn.y,
                btn.x + BUTTON_W,
                btn.y + BUTTON_H,
                bgColor
            );

            // Border
            graphics.fill(
                btn.x,
                btn.y,
                btn.x + BUTTON_W,
                btn.y + 1,
                GuiColors.darken(btn.color, 0.3f)
            );
            graphics.fill(
                btn.x,
                btn.y + BUTTON_H - 1,
                btn.x + BUTTON_W,
                btn.y + BUTTON_H,
                GuiColors.darken(btn.color, 0.3f)
            );

            // Text
            graphics.drawCenteredString(
                font,
                btn.text,
                btn.x + BUTTON_W / 2,
                btn.y + (BUTTON_H - 8) / 2,
                GuiColors.TEXT_WHITE
            );
        }
    }

    // ==================== STATUS HELPERS ====================

    private record StatusIcon(String letter, int color) {}

    private List<StatusIcon> buildStatusList() {
        List<StatusIcon> statuses = new ArrayList<>();

        if (slave.isTiedUp()) statuses.add(new StatusIcon("B", COLOR_BOUND));
        if (slave.isGagged()) statuses.add(new StatusIcon("G", COLOR_GAGGED));
        if (slave.isBlindfolded()) statuses.add(
            new StatusIcon("X", COLOR_BLIND)
        );
        if (slave.hasEarplugs()) statuses.add(new StatusIcon("D", COLOR_DEAF));
        if (slave.hasCollar()) statuses.add(new StatusIcon("C", COLOR_COLLAR));
        if (slave.hasMittens()) statuses.add(
            new StatusIcon("M", COLOR_MITTENS)
        );

        return statuses;
    }

    private boolean hasShockCollar() {
        if (!slave.hasCollar()) return false;
        ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
        return CollarHelper.canShock(collar);
    }

    private boolean hasGPSCollar() {
        if (!slave.hasCollar()) return false;
        ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
        return CollarHelper.hasGPS(collar);
    }

    private boolean isInAnySafeZone(
        ItemStack collarStack,
        LivingEntity entity
    ) {
        if (!CollarHelper.isActive(collarStack)) return true;

        // Read safe spots from NBT
        net.minecraft.nbt.CompoundTag tag = collarStack.getTag();
        if (tag == null || !tag.contains("safeSpots", net.minecraft.nbt.Tag.TAG_LIST)) return true;
        net.minecraft.nbt.ListTag safeSpots = tag.getList("safeSpots", net.minecraft.nbt.Tag.TAG_COMPOUND);
        if (safeSpots.isEmpty()) return true;

        for (int i = 0; i < safeSpots.size(); i++) {
            net.minecraft.nbt.CompoundTag spot = safeSpots.getCompound(i);
            double x = spot.getDouble("x");
            double y = spot.getDouble("y");
            double z = spot.getDouble("z");
            int radius = spot.contains("radius") ? spot.getInt("radius") : 50;
            double dist = entity.distanceToSqr(x, y, z);
            if (dist <= (double) radius * radius) {
                return true;
            }
        }
        return false;
    }

    // ==================== INPUT HANDLING ====================

    @Override
    public boolean mouseClicked(double mouseX, double mouseY, int button) {
        if (button != 0 || cachedButtons == null) return false;

        for (ButtonInfo btn : cachedButtons) {
            if (
                mouseX >= btn.x &&
                mouseX < btn.x + BUTTON_W &&
                mouseY >= btn.y &&
                mouseY < btn.y + BUTTON_H
            ) {
                btn.action.run();
                return true;
            }
        }
        return false;
    }

    @Override
    public List<? extends GuiEventListener> children() {
        return ImmutableList.of();
    }

    @Override
    public List<? extends NarratableEntry> narratables() {
        return ImmutableList.of();
    }
}
