package com.example.stolzaddons;

import JinRyuu.JRMCore.ComJrmcaBonus;
import JinRyuu.JRMCore.JRMCoreH;
import JinRyuu.JRMCore.entity.EntityNPCshadow;
import com.example.stolzaddons.PlanetLogic.*;
import com.example.stolzaddons.blocks.BlockBlackstarDB;
import com.example.stolzaddons.commands.CommandCreatePlanet;
import com.example.stolzaddons.commands.RestorePlanetCommand;
import com.example.stolzaddons.entity.EntityDragonGT;
import com.example.stolzaddons.entityspawning.EntityHandler;
import com.example.stolzaddons.fusions.*;
import com.example.stolzaddons.helpers.EventBusHelper;
import com.example.stolzaddons.network.StolzNetwork;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.network.NetworkRegistry;
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSlab;
import net.minecraft.block.BlockTrapDoor;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemAxe;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import net.minecraft.world.WorldProvider;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.event.entity.living.LivingHurtEvent;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

@Mod(modid = "stolzaddons", name = "DBC Additions Mod", version = "1.0")
public class stolzaddons {

    private final Map<String, Map<String, Integer>> lastPotionLevels = new HashMap<>();
    // Cache of active weapon multipliers per player
    public Map<String, Map<String, Integer>> lastWeaponLevels = new HashMap<>();

    // Cache of active armor multipliers per player
    public Map<String, Map<String, Integer>> lastArmorLevels = new HashMap<>();

    @Mod.Instance("stolzaddons")
    public static stolzaddons instance;
    public static Block blackstarDB;
    public static ItemPotaraLeft potaraLeft;
    public static ItemPotaraRight potaraRight;
    public static final int PLANET_LIST_GUI_ID = 0;

    @SidedProxy(clientSide = "com.example.stolzaddons.client.ClientProxy",
            serverSide = "com.example.stolzaddons.CommonProxy")
    public static CommonProxy proxy;

    public static SimpleNetworkWrapper NETWORK;

    @Mod.EventHandler
    public void preInit(FMLPreInitializationEvent event) {
        File configFile = event.getSuggestedConfigurationFile();
        Configuration config = new Configuration(configFile);


        // Client proxy handles client-only init (keybindings, GUI, etc.)
        proxy.preInit(event);
        proxy.registerPackets(NETWORK);
        // Network setup
        StolzNetwork.init();

        PlanetRegistry.initPlanets();
        PlanetRegistry.initPlanets();

        // Register dimensions for both sides
        proxy.registerPlanetDimensions();



        // Server-side event handlers
        MinecraftForge.EVENT_BUS.register(new EntityRemovalHandler());
        EventBusHelper.register(new EntityHandler());

        // Items
        potaraLeft = new ItemPotaraLeft();
        GameRegistry.registerItem(potaraLeft, "LeftPotara");
        potaraRight = new ItemPotaraRight();
        GameRegistry.registerItem(potaraRight, "RightPotara");

        // Blocks
        blackstarDB = new BlockBlackstarDB();
        GameRegistry.registerBlock(blackstarDB, "blackstarDB");

        // Register entities
        registerEntities();


    }

    private void registerEntities() {
        int dragonID = 1001; // unique ID
        EntityRegistry.registerModEntity(
                EntityDragonGT.class,
                "DragonGT",
                dragonID,
                instance,
                64, 1, true
        );
    }

    @Mod.EventHandler
    public void init(FMLInitializationEvent event) {

        // Client proxy handles client-only init (key input, GUI)
        proxy.init(event);
        //Events
        EventBusHelper.register(new FusionHandler());
        EventBusHelper.register(new PlayerUpdateListener());
        EventBusHelper.register(new NamekianFusionHandler());
        EventBusHelper.register(new ZenkaiListener());
        EventBusHelper.register(new MajinAbsorbtionBonus());


        // GUIs
        NetworkRegistry.INSTANCE.registerGuiHandler(instance, new PlanetGuiHandler());
        // Dimension/planet setup
        int netherId = 1;
        DimensionManager.unregisterProviderType(netherId);
        DimensionManager.registerProviderType(netherId, NetherProvider.class, true);
        if (!DimensionManager.isDimensionRegistered(netherId)) {
            DimensionManager.registerDimension(netherId, netherId);
        }
        MinecraftForge.EVENT_BUS.register(new PlanetLoginHandler());
        FMLCommonHandler.instance().bus().register(new WorldWrap());
        EventBusHelper.register(new WorldWrap());
        EventBusHelper.register(new PlanetDeathReturnHandler());
        //End of planet

        // Other event handlers

        //recipies
        ModRecipes.init();
    }

    @Mod.EventHandler
    public void onServerStarting(FMLServerStartingEvent event) {
        event.registerServerCommand(new RestorePlanetCommand());
        event.registerServerCommand(new CommandCreatePlanet());
    }

    // ------------------------- Player Update Listener -------------------------
    public class PlayerUpdateListener {
        @SubscribeEvent
        public void onPlayerTick(TickEvent.PlayerTickEvent event) {
            if (event.phase != TickEvent.Phase.END) return;
            if (!(event.player instanceof EntityPlayerMP)) return;

            EntityPlayerMP player = (EntityPlayerMP) event.player;
            boolean periodicCheck = player.ticksExisted % 20 == 0;
            if (!periodicCheck) return;

            String playerName = player.getCommandSenderName();
            int race = JRMCoreH.getByte(player, "jrmcRace");

            // --- POTIONS ---
            if ((race == JRMCoreH.RACE_ARCOSIAN || race == JRMCoreH.RACE_MAJIN) && player.isInWater()) {
                player.addPotionEffect(new PotionEffect(Potion.waterBreathing.id, 100, 0));
            }

            Map<String, Integer> potionCache = stolzaddons.instance.lastPotionLevels.computeIfAbsent(playerName, k -> new HashMap<>());
            checkPotion(player, Potion.damageBoost, new int[]{0, 1, 3}, potionCache, 1.3f);
            checkPotion(player, Potion.resistance, new int[]{1}, potionCache, 1.5f);
            checkPotion(player, Potion.moveSpeed, new int[]{1, 4}, potionCache, 1.3f);
            checkPotion(player, Potion.field_76444_x, new int[]{2}, potionCache, 1.5f);
            checkPotion(player, Potion.regeneration, new int[]{2, 5}, potionCache, 1.3f);

            checkPotion(player, Potion.weakness, new int[]{0, 1, 3}, potionCache, 0.7f);
            checkPotion(player, Potion.moveSlowdown, new int[]{1, 4}, potionCache, 0.5f);
            checkPotion(player, Potion.wither, new int[]{0, 1, 2, 3, 5}, potionCache, 0.5f);
            checkPotion(player, Potion.poison, new int[]{2, 5}, potionCache, 0.5f);

            // --- WEAPON BONUS ---
            Map<String, Integer> weaponCache = stolzaddons.instance.lastWeaponLevels.computeIfAbsent(playerName, k -> new HashMap<>());
            ItemStack held = player.getHeldItem();
            if (held != null && isWeapon(held)) {
                float multi = getWeaponMultiplier(held);
                String key = "weapon:" + held.getUnlocalizedName();
                String multiStr = "*" + Math.round(multi * 100) / 100.0;

                Integer lastLevel = weaponCache.get(key);
                int newLevel = Math.round(multi * 100);

                if (lastLevel == null || lastLevel != newLevel) {
                    applyBonus(player, 0, key, multiStr);
                    weaponCache.put(key, newLevel);
                }
            } else {
                for (String key : weaponCache.keySet()) removeBonus(player, 0, key);
                weaponCache.clear();
            }

            // --- ARMOR BONUS ---
            Map<String, Integer> armorCache = stolzaddons.instance.lastArmorLevels.computeIfAbsent(playerName, k -> new HashMap<>());
            // Apply bonuses first
            for (ItemStack armor : player.inventory.armorInventory) {
                if (armor != null && isArmor(armor)) {
                    float multi = getArmorMultiplier(armor);
                    String key = "armor:" + armor.getUnlocalizedName();
                    String multiStr = "*" + Math.round(multi * 100) / 100.0;

                    Integer lastLevel = armorCache.get(key);
                    int newLevel = Math.round(multi * 100);

                    if (lastLevel == null || lastLevel != newLevel) {
                        applyBonus(player, 1, key, multiStr);
                        armorCache.put(key, newLevel);
                    }
                }
            }

            // Remove bonuses for unequipped armor
            armorCache.keySet().removeIf(key -> {
                boolean stillEquipped = false;
                for (ItemStack armor : player.inventory.armorInventory) {
                    if (armor != null && key.equals("armor:" + armor.getUnlocalizedName())) {
                        stillEquipped = true;
                        break;
                    }
                }
                if (!stillEquipped) removeBonus(player, 1, key);
                return !stillEquipped;
            });
        }

        // --- HELPER FUNCTIONS ---
        private boolean isWeapon(ItemStack stack) {
            return stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemAxe;
        }

        private boolean isArmor(ItemStack stack) {
            return stack.getItem() instanceof ItemArmor;
        }

        private float getWeaponMultiplier(ItemStack stack) {
            float base = 1.0f;

            // Durability tiers
            if (stack.getMaxDamage() > 0) {
                float dur = (float)(stack.getMaxDamage() - stack.getItemDamage()) / stack.getMaxDamage();
                if (dur > 0.66f) base = 1.1f;
                else if (dur > 0.33f) base = 1.15f;
                else base = 1.2f;
            }

            // Sharpness
            int sharpLevel = EnchantmentHelper.getEnchantmentLevel(Enchantment.sharpness.effectId, stack);
            base += 0.05f * sharpLevel;

            // Cap at ~1.3
            if (base > 1.3f) base = 1.3f;

            return base;
        }

        private float getArmorMultiplier(ItemStack stack) {
            float base = 1.0f;

            // Durability tiers
            if (stack.getMaxDamage() > 0) {
                float dur = (float)(stack.getMaxDamage() - stack.getItemDamage()) / stack.getMaxDamage();
                if (dur > 0.66f) base = 1.1f;
                else if (dur > 0.33f) base = 1.15f;
                else base = 1.2f;
            }

            // Protection enchantments
            int protLevel = EnchantmentHelper.getEnchantmentLevel(Enchantment.protection.effectId, stack);
            base += 0.05f * protLevel;

            // Cap at 1.3
            if (base > 1.3f) base = 1.3f;

            return base;
        }

        // --- POTION CHECK LOGIC ---
        private void checkPotion(EntityPlayerMP player, Potion potion, int[] attributes, Map<String, Integer> cache, float basemulti) {
            PotionEffect effect = player.getActivePotionEffect(potion);
            int level = (effect != null) ? effect.getAmplifier() + 1 : 0;

            String bonusKey = potion.getName().replace("potion.", "").toLowerCase();
            String multiplier = "*" + Math.ceil(Math.pow(basemulti, level) * 10.0) / 10.0;

            for (int attr : attributes) {
                String cacheKey = attr + ":" + potion.id;
                Integer lastLevel = cache.get(cacheKey);
                if (lastLevel == null) lastLevel = 0;

                if (level != lastLevel) {
                    if (level > 0) applyBonus(player, attr, bonusKey, multiplier);
                    else removeBonus(player, attr, bonusKey);
                    cache.put(cacheKey, level);
                }
            }
        }

        // --- ORIGINAL APPLY / REMOVE BONUS ---
        private void applyBonus(EntityPlayerMP p, int attr, String bonusKey, String multiplier) {
            NBTTagCompound nbt = JRMCoreH.nbt(p, "pres");
            String key = "jrmcAttrBonus" + ComJrmcaBonus.ATTRIBUTES_SHORT[attr];
            String cur = nbt.getString(key);

            if (!cur.isEmpty()) {
                for (String s : cur.split("\\|")) {
                    if (s.equals(bonusKey + ";" + multiplier)) return;
                }
            }

            String entry = bonusKey + ";" + multiplier;
            nbt.setString(key, cur.isEmpty() ? entry : cur + "|" + entry);
        }

        private void removeBonus(EntityPlayerMP p, int attr, String bonusKey) {
            NBTTagCompound nbt = JRMCoreH.nbt(p, "pres");
            String key = "jrmcAttrBonus" + ComJrmcaBonus.ATTRIBUTES_SHORT[attr];
            String cur = nbt.getString(key);

            if (cur.isEmpty()) return;

            StringBuilder sb = new StringBuilder();
            for (String s : cur.split("\\|")) {
                if (!s.startsWith(bonusKey + ";")) {
                    if (sb.length() > 0) sb.append("|");
                    sb.append(s);
                }
            }
            nbt.setString(key, sb.toString());
        }

        @SubscribeEvent
        public void onPlayerDamaged(LivingHurtEvent event) {
            if (!(event.entityLiving instanceof EntityPlayer)) return;

            EntityPlayer player = (EntityPlayer) event.entityLiving;
            DamageSource source = event.source;

            float currentHp = JRMCoreH.getFloat(player, "jrmcBdy");
            float percent = 0f;

            if (source == DamageSource.inFire || source == DamageSource.onFire) percent = 0.05f;
            else if (source == DamageSource.lava) percent = 0.15f;
            else if (source == DamageSource.magic) percent = 0.05f;
            else if (source == DamageSource.drown) percent = 0.10f;
            else return;

            float dbcDamage = currentHp * percent;
            float newHp = currentHp - dbcDamage;
            JRMCoreH.setFloat(newHp, player, "jrmcBdy");

            event.setCanceled(true);
        }
    }

    // ------------------ Entity Removal Handler ------------------
    public static class EntityRemovalHandler {
        @SubscribeEvent
        public void onLivingUpdate(LivingEvent.LivingUpdateEvent event) {
            Entity entity = event.entity;
            if (!(entity instanceof EntityNPCshadow)) return;

            EntityNPCshadow shadow = (EntityNPCshadow) entity;
            World world = shadow.worldObj;
            int radius = 10;
            int entityX = (int) shadow.posX;
            int entityY = (int) shadow.posY;
            int entityZ = (int) shadow.posZ;
            boolean unsafeBlockFound = false;

            for (int dx = -radius; dx <= radius && !unsafeBlockFound; dx++) {
                for (int dy = -radius; dy <= radius && !unsafeBlockFound; dy++) {
                    for (int dz = -radius; dz <= radius && !unsafeBlockFound; dz++) {
                        int checkX = entityX + dx;
                        int checkY = entityY + dy;
                        int checkZ = entityZ + dz;
                        Block block = world.getBlock(checkX, checkY, checkZ);
                        if (block instanceof BlockSlab || block instanceof BlockTrapDoor) {
                            unsafeBlockFound = true;
                        }
                    }
                }
            }

            if (unsafeBlockFound) shadow.setDead();
        }
    }
}
