package com.tiedup.remake.compat.wildfire;

import com.tiedup.remake.compat.wildfire.physics.NpcBreastPhysics;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.entities.AbstractTiedUpNpc;
import com.tiedup.remake.entities.skins.Gender;
import com.wildfire.main.GenderPlayer;
import com.wildfire.main.WildfireGender;
import java.util.Map;
import java.util.WeakHashMap;
import net.minecraft.world.entity.LivingEntity;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.fml.ModList;
import org.apache.commons.lang3.tuple.Pair;

/**
 * Compatibility module for Wildfire's Female Gender Mod.
 */
public class WildfireCompat {

    public static final String MOD_ID = "wildfire_gender";
    private static boolean loaded = false;

    // Cache for NPC physics to maintain state between frames
    // Pair<Left, Right>
    private static final Map<
        LivingEntity,
        Pair<NpcBreastPhysics, NpcBreastPhysics>
    > NPC_PHYSICS_CACHE = new WeakHashMap<>();

    public static void init() {
        loaded = ModList.get().isLoaded(MOD_ID);
        if (loaded) {
            TiedUpMod.LOGGER.info(
                "[Wildfire Compat] Wildfire's Gender Mod detected! Enabling physics."
            );
        }
    }

    public static boolean isLoaded() {
        return loaded;
    }

    /**
     * Get physics instances for an entity. Creates them if needed.
     */
    public static Pair<NpcBreastPhysics, NpcBreastPhysics> getNpcPhysics(
        LivingEntity entity,
        GenderPlayer genderPlayer
    ) {
        if (!loaded || genderPlayer == null) return null;

        return NPC_PHYSICS_CACHE.computeIfAbsent(entity, e -> {
            return Pair.of(
                new NpcBreastPhysics(genderPlayer),
                new NpcBreastPhysics(genderPlayer)
            );
        });
    }

    // Default values based on user config (displayed values)
    // bust size: 100% = 0.8 raw (Wildfire max)
    // separation: -2 displayed = -0.2 raw
    // height: 0, depth: 0, rotation: 0
    // dualphysics: yes = uniboob: false
    private static final float DEFAULT_BUST_SIZE = 0.8f;
    private static final float DEFAULT_SEPARATION = -0.2f; // -2 displayed
    private static final float DEFAULT_BOUNCE = 0.34f;
    private static final float DEFAULT_FLOPPY = 0.75f;

    /**
     * Get or create GenderPlayer data for any entity with a UUID.
     * Wildfire mod uses UUIDs key, so it works for non-players too!
     * Respects entity gender (MALE/FEMALE) and randomizes bust size for females.
     */
    public static GenderPlayer getGenderInfo(LivingEntity entity) {
        if (!loaded) return null;
        try {
            GenderPlayer plr = WildfireGender.getOrAddPlayerById(
                entity.getUUID()
            );

            if (plr != null && entity instanceof AbstractTiedUpNpc damsel) {
                // Determine target gender from entity
                Gender modGender = damsel.getGender();
                GenderPlayer.Gender targetGender = (modGender == Gender.MALE)
                    ? GenderPlayer.Gender.MALE
                    : GenderPlayer.Gender.FEMALE;

                if (plr.getGender() != targetGender) {
                    plr.updateGender(targetGender);
                }

                // Initialize breast settings for females if not yet configured
                // Check if bust size is at Wildfire default (0.6) which means not configured by us
                if (targetGender == GenderPlayer.Gender.FEMALE) {
                    float currentBust = plr.getBustSize();
                    // Wildfire default is 0.6f - if close to that, we haven't configured yet
                    boolean needsInit = Math.abs(currentBust - 0.6f) < 0.01f;

                    if (needsInit) {
                        long seed = entity.getUUID().getLeastSignificantBits();
                        java.util.Random rand = new java.util.Random(seed);

                        // Bust size: 0.8 (100%) with slight random variation ±0.1
                        // Clamped to Wildfire max of 0.8
                        float bustVariation = (rand.nextFloat() - 0.5f) * 0.2f; // -0.1 to +0.1
                        float randomSize = Math.min(
                            0.8f,
                            DEFAULT_BUST_SIZE + bustVariation
                        );
                        randomSize = Math.max(0.4f, randomSize); // min 50% displayed
                        plr.updateBustSize(randomSize);

                        // Dual physics enabled (uniboob = false)
                        plr.getBreasts().updateUniboob(false);

                        // Separation: -0.2 raw (-2 displayed) with slight variation ±0.05
                        float sepVariation = (rand.nextFloat() - 0.5f) * 0.1f; // -0.05 to +0.05
                        float separation = DEFAULT_SEPARATION + sepVariation;
                        separation = Math.max(-0.3f, Math.min(0f, separation)); // clamp -3 to 0 displayed
                        plr.getBreasts().updateXOffset(separation);

                        // Height: 0 (no variation for now)
                        plr.getBreasts().updateYOffset(0f);

                        // Depth: 0 (no variation for now)
                        plr.getBreasts().updateZOffset(0f);

                        // Cleavage/rotation: 0
                        plr.getBreasts().updateCleavage(0f);

                        // Physics: slight random variation around defaults
                        float bounceVariation =
                            (rand.nextFloat() - 0.5f) * 0.1f;
                        float bounce = Math.max(
                            0.2f,
                            Math.min(0.5f, DEFAULT_BOUNCE + bounceVariation)
                        );
                        plr.updateBounceMultiplier(bounce);

                        float floppyVariation =
                            (rand.nextFloat() - 0.5f) * 0.2f;
                        float floppy = Math.max(
                            0.5f,
                            Math.min(1.0f, DEFAULT_FLOPPY + floppyVariation)
                        );
                        plr.updateFloppiness(floppy);

                        // Enable breast physics
                        plr.updateBreastPhysics(true);
                    }
                }
            }
            return plr;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Force an entity to be female in Wildfire system.
     * Also initializes physics/randomization if needed.
     */
    public static void setGenderFemale(LivingEntity entity) {
        if (!loaded) return;
        try {
            GenderPlayer plr = WildfireGender.getOrAddPlayerById(
                entity.getUUID()
            );
            if (plr != null) {
                boolean changed = false;
                if (plr.getGender() != GenderPlayer.Gender.FEMALE) {
                    plr.updateGender(GenderPlayer.Gender.FEMALE);
                    changed = true;
                }

                // Initialize if gender changed or bust is at Wildfire default
                float currentBust = plr.getBustSize();
                boolean needsInit =
                    changed || Math.abs(currentBust - 0.6f) < 0.01f;

                if (needsInit) {
                    long seed = entity.getUUID().getLeastSignificantBits();
                    java.util.Random rand = new java.util.Random(seed);

                    // Same logic as getGenderInfo
                    float bustVariation = (rand.nextFloat() - 0.5f) * 0.2f;
                    float randomSize = Math.min(
                        0.8f,
                        DEFAULT_BUST_SIZE + bustVariation
                    );
                    randomSize = Math.max(0.4f, randomSize);
                    plr.updateBustSize(randomSize);

                    plr.getBreasts().updateUniboob(false);

                    float sepVariation = (rand.nextFloat() - 0.5f) * 0.1f;
                    float separation = Math.max(
                        -0.3f,
                        Math.min(0f, DEFAULT_SEPARATION + sepVariation)
                    );
                    plr.getBreasts().updateXOffset(separation);

                    plr.getBreasts().updateYOffset(0f);
                    plr.getBreasts().updateZOffset(0f);
                    plr.getBreasts().updateCleavage(0f);

                    float bounceVariation = (rand.nextFloat() - 0.5f) * 0.1f;
                    float bounce = Math.max(
                        0.2f,
                        Math.min(0.5f, DEFAULT_BOUNCE + bounceVariation)
                    );
                    plr.updateBounceMultiplier(bounce);

                    float floppyVariation = (rand.nextFloat() - 0.5f) * 0.2f;
                    float floppy = Math.max(
                        0.5f,
                        Math.min(1.0f, DEFAULT_FLOPPY + floppyVariation)
                    );
                    plr.updateFloppiness(floppy);

                    plr.updateBreastPhysics(true);
                }
            }
        } catch (Exception e) {
            TiedUpMod.LOGGER.debug(
                "[WildfireCompat] Failed to update breast physics",
                e
            );
        }
    }

    /**
     * Register render layers for Wildfire compatibility.
     *
     * Adds WildfireDamselLayer to PlayerRenderers to render bondage textures
     * on top of Wildfire's breast geometry for players.
     *
     * NOTE: TiedUp entities (Damsel, Kidnapper, etc.) have their WildfireDamselLayer
     * added directly in DamselRenderer's constructor.
     */
    @OnlyIn(Dist.CLIENT)
    public static void registerRenderLayers(
        EntityRenderersEvent.AddLayers event
    ) {
        if (!loaded) return;

        // Add WildfireDamselLayer to player renderers for breast-aware bind rendering
        // This renders bind textures ON TOP of Wildfire's breast geometry

        // Default player renderer (Steve)
        net.minecraft.client.renderer.entity.EntityRenderer<
            ? extends net.minecraft.world.entity.player.Player
        > defaultRenderer = event.getSkin("default");

        if (
            defaultRenderer instanceof
                net.minecraft.client.renderer.entity.player.PlayerRenderer playerRenderer
        ) {
            playerRenderer.addLayer(
                new com.tiedup.remake.compat.wildfire.render.WildfireDamselLayer<>(
                    playerRenderer,
                    event.getEntityModels()
                )
            );
            TiedUpMod.LOGGER.info(
                "[Wildfire Compat] Added WildfireDamselLayer to default player renderer"
            );
        }

        // Slim player renderer (Alex)
        net.minecraft.client.renderer.entity.EntityRenderer<
            ? extends net.minecraft.world.entity.player.Player
        > slimRenderer = event.getSkin("slim");

        if (
            slimRenderer instanceof
                net.minecraft.client.renderer.entity.player.PlayerRenderer playerRenderer
        ) {
            playerRenderer.addLayer(
                new com.tiedup.remake.compat.wildfire.render.WildfireDamselLayer<>(
                    playerRenderer,
                    event.getEntityModels()
                )
            );
            TiedUpMod.LOGGER.info(
                "[Wildfire Compat] Added WildfireDamselLayer to slim player renderer"
            );
        }
    }
}
