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

import com.mojang.blaze3d.platform.InputConstants;
import com.tiedup.remake.client.ModKeybindings;
import com.tiedup.remake.client.gui.util.GuiColors;
import com.tiedup.remake.client.gui.util.GuiLayoutConstants;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.network.ModNetwork;
import com.tiedup.remake.network.minigame.PacketContinuousStruggleHold;
import com.tiedup.remake.network.minigame.PacketContinuousStruggleStop;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.lwjgl.glfw.GLFW;

/**
 * Client-side GUI for the Continuous Struggle mini-game.
 *
 * Layout:
 * ┌─────────────────────────────────────┐
 * │          STRUGGLING...              │
 * │                                     │
 * │            ┌─────┐                  │
 * │            │  ↑  │  ← Arrow         │
 * │            │  W  │    direction     │
 * │            └─────┘                  │
 * │                                     │
 * │  HOLD [W] to struggle!              │
 * │                                     │
 * │  ████████████░░░░░░░░░  45/100     │  ← Progress bar
 * │                                     │
 * │  Press ESC to stop                  │
 * └─────────────────────────────────────┘
 */
@OnlyIn(Dist.CLIENT)
public class ContinuousStruggleMiniGameScreen extends BaseScreen {

    private UUID sessionId;
    private int currentDirection;
    private int currentResistance;
    private int maxResistance;
    private boolean isLocked;

    // Visual state
    private float animatedResistance;
    private int flashTicks;
    private boolean showDirectionChangeFlash;
    private boolean showShockEffect;
    private int shockEffectTicks;
    private boolean gameComplete;
    private boolean gameSuccess;
    private int completionDelayTicks;

    // Input state
    private int heldDirection = -1;
    private int ticksSinceLastUpdate = 0;

    // Colors
    private static final int ARROW_BG_NORMAL = 0xFF444444;
    private static final int ARROW_BG_HOLDING = 0xFF006600;
    private static final int ARROW_BG_WRONG = 0xFF664400;
    private static final int ARROW_BG_SHOCKED = 0xFF660000;
    private static final int ARROW_TEXT_COLOR = 0xFFFFFFFF;
    private static final int PROGRESS_BAR_BG = 0xFF442222;
    private static final int PROGRESS_BAR_FILL = 0xFF44AA44;
    private static final int PROGRESS_BAR_EMPTY = 0xFFAA4444;

    // Update interval (every 5 ticks = 4 times per second)
    private static final int UPDATE_INTERVAL_TICKS = 5;

    public ContinuousStruggleMiniGameScreen(
        UUID sessionId,
        int currentDirection,
        int currentResistance,
        int maxResistance,
        boolean isLocked
    ) {
        super(Component.translatable("gui.tiedup.continuous_struggle"));
        this.sessionId = sessionId;
        this.currentDirection = currentDirection;
        this.currentResistance = currentResistance;
        this.maxResistance = maxResistance;
        this.isLocked = isLocked;
        this.animatedResistance = currentResistance;
    }

    @Override
    protected int getPreferredWidth() {
        return GuiLayoutConstants.getResponsiveWidth(
            this.width,
            0.5f,
            280,
            350
        );
    }

    @Override
    protected int getPreferredHeight() {
        return GuiLayoutConstants.getResponsiveHeight(
            this.height,
            0.45f,
            200,
            280
        );
    }

    @Override
    protected void init() {
        super.init();
        TiedUpMod.LOGGER.info(
            "[ContinuousStruggleMiniGameScreen] Screen initialized: dir={}, resistance={}/{}, locked={}",
            currentDirection,
            currentResistance,
            maxResistance,
            isLocked
        );
    }

    // ==================== STATE UPDATES FROM SERVER ====================

    /**
     * Called when direction changes.
     */
    public void onDirectionChange(int newDirection) {
        this.currentDirection = newDirection;
        this.showDirectionChangeFlash = true;
        this.flashTicks = 15;

        TiedUpMod.LOGGER.debug(
            "[ContinuousStruggleMiniGameScreen] Direction changed to {}",
            getDirectionKeyName(newDirection)
        );
    }

    /**
     * Called when resistance updates.
     */
    public void onResistanceUpdate(int newResistance) {
        this.currentResistance = newResistance;
    }

    /**
     * Called when shock collar triggers.
     */
    public void onShock() {
        this.showShockEffect = true;
        this.shockEffectTicks = 20;
    }

    /**
     * Called when player escapes successfully.
     */
    public void onEscape() {
        this.gameComplete = true;
        this.gameSuccess = true;
        this.completionDelayTicks = 40; // 2 seconds
    }

    /**
     * Called when session ends.
     */
    public void onEnd() {
        this.gameComplete = true;
        this.gameSuccess = false;
        this.onClose();
    }

    // ==================== TICK AND RENDER ====================

    @Override
    public void tick() {
        super.tick();

        // Animate resistance bar
        animatedResistance = lerp(animatedResistance, currentResistance, 0.15f);

        // Flash timer
        if (flashTicks > 0) {
            flashTicks--;
            if (flashTicks == 0) {
                showDirectionChangeFlash = false;
            }
        }

        // Shock effect timer
        if (shockEffectTicks > 0) {
            shockEffectTicks--;
            if (shockEffectTicks == 0) {
                showShockEffect = false;
            }
        }

        // Completion delay
        if (gameComplete && completionDelayTicks > 0) {
            completionDelayTicks--;
            if (completionDelayTicks <= 0) {
                this.onClose();
            }
            return; // Don't send updates after game complete
        }

        // Check held keys and send updates to server
        if (!gameComplete) {
            updateHeldDirection();

            ticksSinceLastUpdate++;
            if (ticksSinceLastUpdate >= UPDATE_INTERVAL_TICKS) {
                ticksSinceLastUpdate = 0;
                sendHoldUpdate();
            }
        }
    }

    /**
     * Check which direction key is currently held.
     * Uses InputConstants.isKeyDown() directly because keyMapping.isDown()
     * doesn't work properly when a Screen is open.
     */
    private void updateHeldDirection() {
        if (this.minecraft == null) return;

        long windowHandle = this.minecraft.getWindow().getWindow();
        int newHeldDirection = -1;

        // Check each direction key using direct GLFW input
        for (int i = 0; i < 4; i++) {
            KeyMapping keyMapping = ModKeybindings.getStruggleDirectionKey(i);
            if (keyMapping != null) {
                // Get the key code from the KeyMapping
                InputConstants.Key key = keyMapping.getKey();
                if (key.getType() == InputConstants.Type.KEYSYM) {
                    if (
                        InputConstants.isKeyDown(windowHandle, key.getValue())
                    ) {
                        newHeldDirection = i;
                        break;
                    }
                }
            }
        }

        this.heldDirection = newHeldDirection;
    }

    /**
     * Send held direction update to server.
     */
    private void sendHoldUpdate() {
        boolean isHolding = heldDirection >= 0;
        ModNetwork.sendToServer(
            new PacketContinuousStruggleHold(
                sessionId,
                heldDirection,
                isHolding
            )
        );
    }

    @Override
    public void render(
        GuiGraphics graphics,
        int mouseX,
        int mouseY,
        float partialTick
    ) {
        try {
            super.render(graphics, mouseX, mouseY, partialTick);

            int centerX = this.width / 2;
            int y = this.topPos + GuiLayoutConstants.TITLE_HEIGHT + 15;

            // Direction arrow
            renderDirectionArrow(graphics, centerX, y);
            y += 80;

            // Hold instruction
            renderHoldInstruction(graphics, centerX, y);
            y += 25;

            // Progress bar
            renderProgressBar(graphics, y);
            y += 40;

            // Lock indicator
            if (isLocked) {
                Component lockText = Component.translatable(
                    "gui.tiedup.continuous_struggle.status.locked"
                ).withStyle(ChatFormatting.RED, ChatFormatting.BOLD);
                int lockWidth = this.font.width(lockText);
                graphics.drawString(
                    this.font,
                    lockText,
                    centerX - lockWidth / 2,
                    y,
                    0xFFFFFFFF
                );
                y += 18;
            }

            // Status / instructions
            renderStatus(graphics, centerX, y);

            // Shock overlay
            if (showShockEffect) {
                renderShockOverlay(graphics);
            }
        } catch (Exception e) {
            TiedUpMod.LOGGER.error(
                "[ContinuousStruggleMiniGameScreen] Render error: ",
                e
            );
        }
    }

    private void renderDirectionArrow(
        GuiGraphics graphics,
        int centerX,
        int y
    ) {
        int arrowSize = 60;
        int arrowX = centerX - arrowSize / 2;

        // Determine background color
        int bgColor;
        if (showShockEffect) {
            bgColor = ARROW_BG_SHOCKED;
        } else if (showDirectionChangeFlash) {
            bgColor = (flashTicks % 4 < 2) ? 0xFF666600 : ARROW_BG_NORMAL;
        } else if (heldDirection == currentDirection) {
            bgColor = ARROW_BG_HOLDING;
        } else if (heldDirection >= 0 && heldDirection != currentDirection) {
            bgColor = ARROW_BG_WRONG;
        } else {
            bgColor = ARROW_BG_NORMAL;
        }

        // Draw arrow box
        graphics.fill(arrowX, y, arrowX + arrowSize, y + arrowSize, bgColor);
        graphics.renderOutline(arrowX, y, arrowSize, arrowSize, 0xFFAAAAAA);

        // Draw arrow symbol
        String arrowSymbol = getDirectionArrow(currentDirection);
        int arrowWidth = this.font.width(arrowSymbol);
        graphics.drawString(
            this.font,
            arrowSymbol,
            centerX - arrowWidth / 2,
            y + 12,
            ARROW_TEXT_COLOR
        );

        // Draw key name
        String keyName = getDirectionKeyName(currentDirection);
        int keyWidth = this.font.width(keyName);
        graphics.drawString(
            this.font,
            keyName,
            centerX - keyWidth / 2,
            y + arrowSize - 20,
            ARROW_TEXT_COLOR
        );
    }

    private void renderHoldInstruction(
        GuiGraphics graphics,
        int centerX,
        int y
    ) {
        String keyName = getDirectionKeyName(currentDirection);
        Component instruction;

        if (showShockEffect) {
            instruction = Component.translatable(
                "gui.tiedup.continuous_struggle.status.shocked"
            ).withStyle(ChatFormatting.RED, ChatFormatting.BOLD);
        } else if (heldDirection == currentDirection) {
            instruction = Component.translatable(
                "gui.tiedup.continuous_struggle.status.struggling"
            ).withStyle(ChatFormatting.GREEN);
        } else {
            instruction = Component.translatable(
                "gui.tiedup.continuous_struggle.hold_key",
                keyName
            ).withStyle(ChatFormatting.YELLOW);
        }

        int instructionWidth = this.font.width(instruction);
        graphics.drawString(
            this.font,
            instruction,
            centerX - instructionWidth / 2,
            y,
            0xFFFFFFFF
        );
    }

    private void renderProgressBar(GuiGraphics graphics, int y) {
        int barWidth = 200;
        int barHeight = 18;
        int barX = (this.width - barWidth) / 2;

        // Calculate progress (inverted: 0 resistance = full progress)
        float progress =
            maxResistance > 0
                ? 1.0f - (animatedResistance / maxResistance)
                : 0.0f;
        progress = Math.max(0.0f, Math.min(1.0f, progress));

        // Background (empty portion)
        graphics.fill(barX, y, barX + barWidth, y + barHeight, PROGRESS_BAR_BG);

        // Fill (progress portion)
        int fillWidth = (int) (barWidth * progress);
        if (fillWidth > 0) {
            graphics.fill(
                barX,
                y,
                barX + fillWidth,
                y + barHeight,
                PROGRESS_BAR_FILL
            );
        }

        // Border
        graphics.renderOutline(barX, y, barWidth, barHeight, 0xFF888888);

        // Text showing resistance
        String progressText = String.format(
            "%d/%d",
            (int) animatedResistance,
            maxResistance
        );
        int textWidth = this.font.width(progressText);
        graphics.drawString(
            this.font,
            progressText,
            barX + barWidth + 8,
            y + 5,
            GuiColors.TEXT_WHITE
        );

        // Label above bar
        Component label = Component.translatable(
            "gui.tiedup.continuous_struggle.label.resistance"
        ).withStyle(ChatFormatting.GRAY);
        graphics.drawString(
            this.font,
            label,
            barX,
            y - 12,
            GuiColors.TEXT_WHITE
        );
    }

    private void renderStatus(GuiGraphics graphics, int centerX, int y) {
        Component status;

        if (gameComplete && gameSuccess) {
            status = Component.translatable(
                "gui.tiedup.continuous_struggle.status.escaped"
            ).withStyle(ChatFormatting.GREEN, ChatFormatting.BOLD);
        } else if (gameComplete) {
            status = Component.translatable(
                "gui.tiedup.continuous_struggle.status.stopped"
            ).withStyle(ChatFormatting.GRAY);
        } else {
            status = Component.translatable(
                "gui.tiedup.continuous_struggle.status.press_esc"
            ).withStyle(ChatFormatting.DARK_GRAY);
        }

        int statusWidth = this.font.width(status);
        graphics.drawString(
            this.font,
            status,
            centerX - statusWidth / 2,
            y,
            0xFFFFFFFF
        );
    }

    private void renderShockOverlay(GuiGraphics graphics) {
        // Red flash overlay
        int alpha = (int) (100 * ((float) shockEffectTicks / 20.0f));
        int overlayColor = (alpha << 24) | 0xFF0000;
        graphics.fill(0, 0, this.width, this.height, overlayColor);
    }

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

    @Override
    public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
        // ESC to stop — onClose() handles sending the stop packet
        if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
            this.onClose();
            return true;
        }

        return super.keyPressed(keyCode, scanCode, modifiers);
    }

    @Override
    public void onClose() {
        // Send stop packet if not already complete
        if (!gameComplete) {
            ModNetwork.sendToServer(
                new PacketContinuousStruggleStop(sessionId)
            );
        }
        super.onClose();
    }

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

    // ==================== HELPERS ====================

    private String getDirectionArrow(int direction) {
        return switch (direction) {
            case 0 -> "\u2191"; // ↑
            case 1 -> "\u2190"; // ←
            case 2 -> "\u2193"; // ↓
            case 3 -> "\u2192"; // →
            default -> "?";
        };
    }

    private String getDirectionKeyName(int direction) {
        return ModKeybindings.getStruggleDirectionKeyName(direction);
    }

    private float lerp(float current, float target, float speed) {
        if (Math.abs(current - target) < 0.5f) {
            return target;
        }
        return current + (target - current) * speed;
    }
}
