package com.zivilon.cinder_loe.util;

import com.zivilon.cinder_loe.CinderLoE;
import com.zivilon.cinder_loe.client.render.item.RenderHelper;
import com.zivilon.cinder_loe.mixins.MixinEntity;
import com.zivilon.cinder_loe.droptables.*;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.UUID;

import lotr.common.LOTRCreativeTabs;
import lotr.common.world.biome.LOTRBiome;
import lotr.common.item.LOTRItemArmor;
import lotr.common.item.LOTRMaterial;
import lotr.common.enchant.LOTREnchantment;
import lotr.common.enchant.LOTREnchantmentHelper;
import lotr.common.entity.npc.LOTREntityNPC;


import net.minecraft.client.renderer.Tessellator;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.BaseAttributeMap;
import net.minecraft.entity.ai.attributes.IAttribute;
import net.minecraft.entity.ai.attributes.IAttributeInstance;
import net.minecraft.entity.ai.attributes.ServersideAttributeMap;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.World;


public class Utilities {

    public static int[] LOTRIntCache = null;
    public static LOTRBiome reflected_river;
    public static LOTRBiome reflected_taiga;
    public static LOTRCreativeTabs reflected_tab_block;
    public static List<String> badEnchants = new ArrayList<>();
    public static Map<String, LOTREnchantment> upgradedEnchants = new HashMap<>();

    static {
        badEnchants.add("weak1");
        badEnchants.add("weak2");
        badEnchants.add("weak3");
        badEnchants.add("weak4");
        badEnchants.add("meleeSlow1");
        badEnchants.add("meleeUnreach1");
        badEnchants.add("toolSlow1");
        badEnchants.add("protectWeak1");
        badEnchants.add("protectWeak2");
        badEnchants.add("rangedWeak1");
        badEnchants.add("rangedWeak2");
        badEnchants.add("rangedWeak3");
        badEnchants.add("protectRangedWeak1");
        badEnchants.add("protectRangedWeak2");
        upgradedEnchants.put("strong1", LOTREnchantment.strong1);
        upgradedEnchants.put("strong2", LOTREnchantment.strong3);
        upgradedEnchants.put("strong3", LOTREnchantment.strong4);
        upgradedEnchants.put("durable1", LOTREnchantment.durable2);
        upgradedEnchants.put("durable2", LOTREnchantment.durable3);
        upgradedEnchants.put("knockback1", LOTREnchantment.knockback2);
        upgradedEnchants.put("toolSpeed1", LOTREnchantment.toolSpeed2);
        upgradedEnchants.put("toolSpeed2", LOTREnchantment.toolSpeed3);
        upgradedEnchants.put("toolSpeed3", LOTREnchantment.toolSpeed4);
        upgradedEnchants.put("looting1", LOTREnchantment.looting2);
        upgradedEnchants.put("looting2", LOTREnchantment.looting3);
        upgradedEnchants.put("protect1", LOTREnchantment.protect2);
        upgradedEnchants.put("protectFire1", LOTREnchantment.protectFire2);
        upgradedEnchants.put("protectFire2", LOTREnchantment.protectFire3);
        upgradedEnchants.put("protectFall1", LOTREnchantment.protectFall2);
        upgradedEnchants.put("protectFall2", LOTREnchantment.protectFall3);
        upgradedEnchants.put("protectRanged1", LOTREnchantment.protectRanged2);
        upgradedEnchants.put("protectRanged2", LOTREnchantment.protectRanged3);
        upgradedEnchants.put("rangedStrong1", LOTREnchantment.rangedStrong2);
        upgradedEnchants.put("rangedStrong2", LOTREnchantment.rangedStrong3);
        upgradedEnchants.put("rangedKnockback1", LOTREnchantment.rangedKnockback2);
    }

    public static void initialize_reflects() {
        try {
            Field river_field = LOTRBiome.class.getField("river");
            reflected_river = (LOTRBiome)river_field.get(null);
            Field taiga_field = LOTRBiome.class.getField("taiga");
            reflected_taiga = (LOTRBiome)taiga_field.get(null);

        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static void pre_init_reflects() {
        try {
            Field tabBlockField = LOTRCreativeTabs.class.getField("tabBlock");
            reflected_tab_block = (LOTRCreativeTabs)tabBlockField.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static EntityPlayerMP getPlayerByUUID(UUID playerUUID) {
        MinecraftServer server = MinecraftServer.getServer(); // Get the server instance
        if (server == null) {
            return null; // Server instance is not available
        }

        // Iterate through the list of online players and match UUID
        for (Object obj : server.getConfigurationManager().playerEntityList) {
            if (obj instanceof EntityPlayerMP) {
                EntityPlayerMP player = (EntityPlayerMP) obj;
                if (player.getUniqueID().equals(playerUUID)) {
                    return player;
                }
            } else {
                System.err.println("[CinderCore] Player list having non-players, WHAT?!?!?!");
            }
        }
        return null; // Player not found or not online
    }

    public static String toSnakeCase(String input) {
        if (input == null || input.isEmpty()) {
            return input;
        }

        StringBuilder builder = new StringBuilder();
        char[] chars = input.toCharArray();

        builder.append(Character.toLowerCase(chars[0]));
        for (int i = 1; i < chars.length; i++) {
            char c = chars[i];
            if (Character.isUpperCase(c)) {
                builder.append('_').append(Character.toLowerCase(c));
            } else {
                builder.append(c);
            }
        }

        return builder.toString();
    }

    public static void writeLog(String message) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("custom_log.txt", true))) {
            LocalDateTime now = LocalDateTime.now();
            String timestamp = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            writer.write(timestamp + " - " + message + "\n");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static EntityPlayer getPlayerByName(String player_name) {
        MinecraftServer server = MinecraftServer.getServer();
        if (server == null) {
            return null;
        }

        for (Object obj : server.getConfigurationManager().playerEntityList) {
    	    if (obj instanceof EntityPlayerMP) {
                EntityPlayer player = (EntityPlayer)obj;
			    String found_name = player.getCommandSenderName();
	            if (found_name.equals(player_name)) {
	                return player;
	            }
            }
        }
        return null; // Player not found or not online
    }

    public static boolean hasFireWeapon(String player_name) {
        EntityPlayer player = getPlayerByName(player_name);

        if (player == null) {
            return false;
        }

        ItemStack held_item = player.getHeldItem();
        boolean has_fire = LOTREnchantmentHelper.hasEnchant(held_item, LOTREnchantment.fire);
        return has_fire;
    }

    public static boolean has_fire_weapon(Entity entity) {
        if (!(entity instanceof EntityLivingBase)) return false;
        ItemStack held_item = ((EntityLivingBase)entity).getHeldItem();
        return LOTREnchantmentHelper.hasEnchant(held_item, LOTREnchantment.fire);
    }

    public static double calculate_melee_damage_on_entity(EntityLivingBase target, EntityLivingBase attacker) {
        if (attacker == null) return 0.0D;
        if (attacker.getEntityAttribute(SharedMonsterAttributes.attackDamage) == null) return 0.0D;

        double attribute = attacker.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue();
        double enchantments = EnchantmentHelper.getEnchantmentModifierLiving(attacker, target);
        boolean is_critical = !attacker.onGround && attacker.motionY < 0.0D && !attacker.isInWater() && !attacker.isRiding();
        double critical = 0.0D;
        if (is_critical) {
            PotionEffect strength_effect = attacker.getActivePotionEffect(Potion.damageBoost);
            if (strength_effect == null) {
                critical = Math.min((attribute + enchantments) * 1.5D, 7.0D);
            } else {
                int level = strength_effect.getAmplifier() + 1;
                critical = Math.min((attribute + enchantments) * 1.5D, 7.0D * (1.0D + 0.5D * level));
            }
        }
        return attribute + enchantments + critical;
    }

    public static boolean isArrayEmpty(int[] array) {
        for (int value : array) {
            if (value != 0) return false;
        }
        return true;
    }

    public static void setInvulnerable(Entity entity, boolean invulnerable) {
        ((MixinEntity)entity).setInvulnerable(invulnerable);
    }

    public static void setLOTREnchant(ItemStack itemstack, LOTREnchantment ench) {
        NBTTagList tags = getItemEnchantTags(itemstack, true);
        if (tags != null) {
            String enchName = ench.enchantName;
            tags.appendTag((NBTBase)new NBTTagString(enchName));
        }
    }

    public static NBTTagList getItemEnchantTags(ItemStack itemstack, boolean create) {
        NBTTagCompound itemData = itemstack.getTagCompound();
        NBTTagList tags = null;
        if (itemData != null && itemData.hasKey("LOTREnch")) {
            tags = itemData.getTagList("LOTREnch", 8);
        } else if (create) {
            if (itemData == null) {
                itemData = new NBTTagCompound();
                itemstack.setTagCompound(itemData);
            }
            tags = new NBTTagList();
            itemData.setTag("LOTREnch", (NBTBase)tags);
        }
        return tags;
    }

    public static void setAppliedRandomEnchants(ItemStack itemstack) {
        if (!itemstack.hasTagCompound())
            itemstack.setTagCompound(new NBTTagCompound());
        itemstack.getTagCompound().setBoolean("LOTRRandomEnch", true);
    }

    public static void knockback(EntityLivingBase target, EntityLivingBase attacker, double knockback_strength) {
        // Calculate direction of knockback
        double dx = attacker.posX - target.posX;
        double dz = attacker.posZ - target.posZ;

        // Normalize the direction vector
        double distance = Math.sqrt(dx * dx + dz * dz);
        if (distance != 0) {
            dx /= distance;
            dz /= distance;
        }

        // Apply knockback manually
        target.motionX -= dx * knockback_strength;
        target.motionZ -= dz * knockback_strength;

        // Ensure the motion is applied
        target.velocityChanged = true;
    }

    public static boolean isBadEnch(LOTREnchantment ench) {
        if (ench == null) return false;
        String ench_name = ench.enchantName;
        if (badEnchants.contains(ench_name)) {
            return true;
        }
        return false;
    }
    public static LOTREnchantment upgradeEnch(ItemStack item, LOTREnchantment ench) {
        if (ench == null) return ench;
        if (ench != LOTREnchantment.protect1) {
            LOTREnchantment return_ench = upgradedEnchants.get(ench.enchantName);
            if (return_ench == null) return_ench = ench;
            return return_ench;
        }
        int armor_piece = ((ItemArmor)item.getItem()).armorType;
        int prot = ((ItemArmor)item.getItem()).damageReduceAmount;
        LOTREnchantment return_value = ench;
        switch(armor_piece) {
            case 0:
                if (prot < 2) return_value = upgradedEnchants.get(ench.enchantName);
                break;
            case 1:
                if (prot < 7) return_value = upgradedEnchants.get(ench.enchantName);
                break;
            case 2:
                if (prot < 5) return_value = upgradedEnchants.get(ench.enchantName);
                break;
            case 3:
                if (prot < 2) return_value = upgradedEnchants.get(ench.enchantName);
                break;
            default:
                break;
        }
        return return_value;
    }
    public static <T> boolean array_contains_array(T[] superarray, T[] subarray) {
        for (T subobj : subarray) {
            if (subobj == null || Arrays.stream(superarray).anyMatch(subobj::equals)) continue;
            return false;
        }
        return true;
    }
    public static Entity find_entity_by_uuid(World world, UUID uuid) {
        List<Entity> snapshot = new ArrayList<>(world.loadedEntityList); // Safe copy
        for (Entity entity : snapshot) {
            if (uuid.equals(entity.getUniqueID())) {
                return entity;
            }
        }
        return null;
    }

    public static void reset_attributes(LOTREntityNPC entity) {
        try {
            BaseAttributeMap new_map = new ServersideAttributeMap();

            Field attr_map_field;
            try {
                attr_map_field = EntityLivingBase.class.getDeclaredField("attributeMap");
            } catch (NoSuchFieldException e) {
                attr_map_field = EntityLivingBase.class.getDeclaredField("field_110155_d");
            }
            attr_map_field.setAccessible(true);
            attr_map_field.set(entity, new_map);

            Method method = find_most_specific_method(entity.getClass(), "applyEntityAttributes", "func_110147_ax");
            if (method == null) throw new RuntimeException("No applyEntityAttributes method found");
            method.setAccessible(true);
            method.invoke(entity);


        } catch (Exception e) {
            throw new RuntimeException("Failed to reset attribute map", e);
        }
    }
    public static void increment_modifier(IAttributeInstance attr, UUID uuid, String name, double amount, int op) {
        AttributeModifier existing = attr.getModifier(uuid);
        double new_amount = amount;

        if (existing != null) {
            new_amount += existing.getAmount();
            attr.removeModifier(existing);
        }

        AttributeModifier updated = new AttributeModifier(uuid, name, new_amount, op);
        attr.applyModifier(updated);
    }
    public static void set_modifier(IAttributeInstance attr, UUID uuid, String name, double amount, int op) {
        AttributeModifier existing = attr.getModifier(uuid);
        if (existing != null) {
            attr.removeModifier(existing);
        }

        AttributeModifier modifier = new AttributeModifier(uuid, name, amount, op);
        attr.applyModifier(modifier);
    }
    public static Method find_most_specific_method(Class<?> cls, String... names) {
        while (cls != null) {
            for (String name : names) {
                try {
                    Method m = cls.getDeclaredMethod(name);
                    if (m.getParameterCount() == 0) return m;
                } catch (NoSuchMethodException ignored) {}
            }
            cls = cls.getSuperclass();
        }
        return null;
    }
}
