import { world, system, GameMode, ItemEnchantableComponent } from "@minecraft/server";
import { Vector3 } from "./vector3.js";
import { JsonDatabase } from "./con-database.js";
import weapons from './../weaponTypes/import.js';
import effects from "../combatEffects/import.js";

let generalis = world.scoreboard

export function generateUniqueId(length) {
    const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    let id = '';
    for (let i = 0; i < length; i++) {
        const randomIndex = Math.floor(Math.random() * characters.length);
        id += characters[randomIndex];
    }
    return Date.now().toString(16) + id;
}

let combo = new JsonDatabase("combo")


/*let enemyDefense = new Map([
    ['netherite_helmet', 3],
    ['netherite_chestplate', 8],
    ['netherite_leggings', 7],
    ['netherite_boots', 3],
    ['diamond_helmet', 3],
    ['diamond_chestplate', 8],
    ['diamond_leggings', 6],
    ['diamond_boots', 3],
    ['iron_helmet', 2],
    ['iron_chestplate', 6],
    ['iron_leggings', 5],
    ['iron_boots', 2],
    ['golden_helmet', 2],
    ['golden_chestplate', 5],
    ['golden_leggings', 3],2
    ['golden_boots', 1],
    ['chainmail_helmet', 2],
    ['chainmail_chestplate', 5],
    ['chainmail_leggings', 4],
    ['chainmail_boots', 1],
    ['leather_helmet', 1],
    ['leather_chestplate', 3],
    ['leather_leggings', 2],
    ['leather_boots', 1],
])*/

export function calculateDamageTaken(player, damage, damageType, entity, item) {
    let damageAdd
    try{
        damageAdd = processWeaponEnchants(getEnchantmentss(item), entity.getComponent("minecraft:type_family").getTypeFamilies())
    } catch(err) {
        damageAdd = 0
    }
    let percentBoost
    let damageIncision = (generalis.getObjective(`${damageType}Resist`).getScore(entity)) / 100
    let EntityPower = JSON.parse(entity.getDynamicProperty("resetingStatus")) ?? {effects: {}}
    let PlayerPower 
    let Strength = generalis.getObjective("Strength").getScore(player)
    try {
        PlayerPower = JSON.parse(player.getDynamicProperty("resetingStatus"))
    } catch(err){
        PlayerPower = {}
    }
    
    try{
        percentBoost = PlayerPower.Boost + PlayerPower[`${damageType}Boost`]
    } catch(error) {
        percentBoost = 0
    }
    let damageReduction
    try{
        damageReduction = EntityPower[damageType] + EntityPower["Alldamage"]
    } catch(error) {
        damageReduction = 0
    }
    let armorShred = EntityPower.effects["AB"] != undefined ? EntityPower.effects["AB"].reductions : 0
    let processingDamage = JSON.parse(damage) + damageAdd + (Strength * 0.02) + JSON.parse(damage * percentBoost)
    let processedDamage = processingDamage - (processingDamage * damageReduction) - (processingDamage * damageIncision)
    let damageTaken = processedDamage - (generalis.getObjective("Endurance").getScore(entity) / 20)
    // Calculate the first term inside the max function: armorPoints / 5
    if(entity.typeId == "minecraft:player") {
        let equippedArmor = getArmor(entity)
        const armorReduction = getArmorPoints(equippedArmor, damageType) / 5;
        
        // Calculate the second term inside the max function
        const toughnessReduction = 4 * processedDamage / (Math.min(getArmorToughnessByTypeId(equippedArmor), 20) + 8);
        const maxReduction = armorReduction - toughnessReduction;
        
        // Determine the value of max(armorReduction, maxReduction)
        const effectiveArmor = Math.max(armorReduction, maxReduction);
        
        // Determine the value of min(20, effectiveArmor)
        const clampedArmor = (Math.min(20, effectiveArmor) - (effectiveArmor * armorShred));
        
        // Calculate the reduction factor
        const reductionFactor = 1 - (clampedArmor / 25);
        
        // Calculate the final damage taken
        damageTaken = processedDamage * reductionFactor - (generalis.getObjective("Endurance").getScore(entity) / 20);
    }
    //console.warn(`damage: ${damage}`)
    //console.warn(`Reduction: ${percentBoost}`)
    //console.warn(`damageIncision: ${damageIncision}`)
    //console.warn(`Strength: ${Strength}`)
    //console.warn(`final damage: ${damageTaken}`)
    return damageTaken;
    
}

function getEnchantmentss(item){
    try{
        let enchantmentsArray = [];
        if(!item.hasComponent("minecraft:enchantable")) return []
        const eCompo = item.getComponent("minecraft:enchantable");
        enchantmentsArray = eCompo.getEnchantments();
        return enchantmentsArray;
    } catch(error) {
        return []
    }     
            
}

function processWeaponEnchants(enchants, families) {
    let bonusDamage = 0
    let DamageBonuses = new Map([
        ["sharpness",1.25],
        ["impaling",2.5],
        ["bane_of_arthropods",2.5],
        ["smite",2.5],
        ["efficiency",1],
    ])
    for(let i = 0;i < enchants.length; i++) {
        try {
            let extra = DamageBonuses.get(enchants[i].type.id)
            if(enchants[i].type.id == "bane_of_arthropods" && (families.includes("spider")||families.includes("cave_spider")||families.includes("silverfish"))) {
                bonusDamage = bonusDamage + (extra * JSON.parse(enchants[i].level))
            } else if(enchants[i].type.id == "smite" && families.includes("undead")) {
                bonusDamage = bonusDamage + (extra * JSON.parse(enchants[i].level))
            } else if(enchants[i].type.id == "sharpness") {
                bonusDamage = bonusDamage + (extra * JSON.parse(enchants[i].level))
            }
           
        } catch(err) {
            continue;
        }
    }
    return bonusDamage;
}

function processArmorEnchants(enchants) {
    let bonusDamage = 0
    let DamageBonuses = new Map([
        ["protection",0.04],
        ["impaling",2.5],
        ["bane_of_arthropods",2.5],
        ["smite",2.5],
        ["efficiency",1],
    ])
    for(let i = 0;i < enchants.length; i++) {
        try {
            let extra = DamageBonuses.get(enchants[i].type.id)
            if(enchants[i].type.id == "protection") {
                bonusDamage = bonusDamage + (extra * JSON.parse(enchants[i].level))
            }
           
        } catch(err) {
            continue;
        }
    }
    return bonusDamage;
}

function getArmorPoints(type, damageType) {
    // Define the armor points for each type
    const armorValues = {
        "minecraft:turtle_helmet": 2,
        "minecraft:leather_helmet": 1,
        "minecraft:leather_chestplate": 3,
        "minecraft:leather_leggings": 2,
        "minecraft:leather_boots": 1,
        "minecraft:golden_helmet": 2,
        "minecraft:golden_chestplate": 5,
        "minecraft:golden_leggings": 3,
        "minecraft:golden_boots": 1,
        "minecraft:chainmail_helmet": 2,
        "minecraft:chainmail_chestplate": 5,
        "minecraft:chainmail_leggings": 4,
        "minecraft:chainmail_boots": 1,
        "minecraft:iron_helmet": 2,
        "minecraft:iron_chestplate": 6,
        "minecraft:iron_leggings": 5,
        "minecraft:iron_boots": 2,
        "minecraft:diamond_helmet": 3,
        "minecraft:diamond_chestplate": 8,
        "minecraft:diamond_leggings": 6,
        "minecraft:diamond_boots": 3,
        "minecraft:netherite_helmet": 3,
        "minecraft:netherite_chestplate": 8,
        "minecraft:netherite_leggings": 6,
        "minecraft:netherite_boots": 3
    };
    
    // Calculate the total armor points
    if(type == []){
        return 0
    }
    let totalArmorPoints = 0;
    for (const item of type) {
        let additionalArmor = 0
        let typer = item.typeId
        switch(damageType){
            case "slash": {
               if(typer.includes("leather")) {
                    additionalArmor = additionalArmor + 2
               } else if(typer.includes("chainmail")||typer.includes("netherite")) {
                    additionalArmor = additionalArmor + 1
               } else if(typer.includes("diamond")) {
                    additionalArmor = additionalArmor - 1
               }
            } break;
            case "blunt": {
                if(typer.includes("netherite")) {
                    additionalArmor = additionalArmor + 2
               } else if(typer.includes("iron")) {
                    additionalArmor = additionalArmor + 1
               }
            } break;
            case "pierce": {
               if(typer.includes("chainmail")) {
                    additionalArmor = additionalArmor + 2
               } else if(typer.includes("diamond")||typer.includes("netherite")) {
                    additionalArmor = additionalArmor - 2
               } else if(typer.includes("iron")) {
                    additionalArmor = additionalArmor - 1
               }
            } break;
        }
        totalArmorPoints += ((armorValues[typer] + (armorValues[typer] * processArmorEnchants(getEnchantmentss(item)))) + additionalArmor) || 0;
    }
    console.warn(totalArmorPoints)
    
    return totalArmorPoints;
}

function getArmorToughnessByTypeId(type) {
    // Define the armor toughness for each armor type
    const armorToughnessValues = {
        "minecraft:diamond_helmet": 2,
        "minecraft:diamond_chestplate": 2,
        "minecraft:diamond_leggings": 2,
        "minecraft:diamond_boots": 2,
        "minecraft:netherite_helmet": 3,
        "minecraft:netherite_chestplate": 3,
        "minecraft:netherite_leggings": 3,
        "minecraft:netherite_boots": 3,
    };
    let totalArmorToughnessValue = 0;
    for (const items of type) {
        totalArmorToughnessValue += armorToughnessValues[items.typeId] || 0;
    }
    // Return the armor toughness for the given typeId
    return totalArmorToughnessValue
}

function getArmor(player) {
    const listOfEquipmentSlots = ["Head", "Chest", "Legs", "Feet"];
    let wornArmor = [];

    for (let i = 0; i < listOfEquipmentSlots.length; i++) {
        const equipment = player.getComponent("equippable")?.getEquipment(listOfEquipmentSlots[i]);
        if (equipment) {
            wornArmor.push(equipment);
        }
    }

    return wornArmor;
}

function getExhaustLimit(player) {
    let exh = world.scoreboard.getObjective("exhaust_lim").getScore(player.id);
    return exh;
}

export function distanceInEnts(position1, position2) {
    const dx = position2.x - position1.x;
    const dy = position2.y - position1.y;
    const dz = position2.z - position1.z;

    const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
    return distance
}

function targetPickerOnlyPlayers(player, Hitbox, range, notargs) {
    let polter
    let geist = []
    polter = Hitbox.dimension.getEntities({ location: Hitbox.location, type: "minecraft:player", excludeFamilies: ["inanimate", "projectiles"], excludeNames: [`${player.name}`],excludeTags:[`${player.typeId}`, "hit"], maxDistance: 3, closest : notargs}).forEach(e => {
        let distancing = Math.abs(distanceInEnts(e.location,Hitbox.location))
        if(distancing <= range) {
          geist.push(e)
        }
    })
 	
    return geist
}

export function EnemyClashPower(player, percent, atkspd){
    let loc = coordExtractor(player, 0)
    let lof = Vector3.Add(loc, Vector3.Scale(player.getViewDirection(), 1));
    let Hit = player.dimension.spawnEntity(`pixel:hit_box<range:line_target>`, lof);
    let target = targetPickerOnlyPlayers(player, Hit, 8, 10)
    let attack = 0
    for(let entity of target) {
        let equipment = entity.getComponent("minecraft:equippable");
        let mainhand = equipment?.getEquipmentSlot(`Mainhand`)?.getItem()
        let bluePower
        try{
            let blue = weaponTypeSplicer(mainhand)
            bluePower = weapons[blue].getCurrentAtkspd(entity, combo)
        } catch(err) {
            bluePower = 0
        }
        if(bluePower < atkspd && (atkspd - bluePower) > 5) {
            bluePower = bluePower + 3
            attack = bluePower
            try{
                system.runTimeout(()=>{
                    entity.dimension.spawnParticle(`combat:hit_indicator`,player.location)
                },(atkspd - (bluePower + 3)))
                system.runTimeout(()=>{
                    player.addTag("parriable")
                },(atkspd - bluePower))
            } catch{}
        } else {
            attack = atkspd
        }
        
    }
    return attack
}

export function BlockBehind(p){
    let loc = coordExtractor(p, 1)
    let lof = Vector3.Add(loc, Vector3.Scale(p.getViewDirection(), -1));
    return p.dimension.getBlock(lof)
}
    
function calculateAngle(v1, v2) {
    const dot = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
    const mag1 = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
    const mag2 = Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z);
    return Math.acos(dot / (mag1 * mag2)) * (180 / Math.PI);
}

export function getEntitiesInAngleView(player,distance,notargs) {
    const viewDir = player.getViewDirection();
    const pLoc = player.location;
    const entities = player.dimension.getEntities({ location: player.location, excludeNames: [player.id], excludeTypes: ["minecraft:item", "pixel:hit_box", "minecraft:arrow", "pixel:aura"], excludeFamilies: ["inanimate", "projectiles"],excludeTags:[`${player.id}`], maxDistance: distance, closest : notargs});
    const inViewEntities = entities.filter(entity => {
        const eLoc = entity.location;
        const toEntityVec = {
            x: eLoc.x - pLoc.x,
            y: eLoc.y - pLoc.y,
            z: eLoc.z - pLoc.z
        };
        const angle = calculateAngle(viewDir, toEntityVec);
        return angle >= -45 && angle <= 45;
    });
    return inViewEntities;
}


export function cooldowns(player,indexNum,cooldown,Times){
        let TimerMaps = JSON.parse(player.getDynamicProperty("entityStatus"))
        TimerMaps.abilityCd[indexNum] = cooldown
        TimerMaps.abilityTimes[indexNum] = Times
        TimerMaps.abilityChecks[indexNum] = 1
        player.setDynamicProperty("entityStatus", JSON.stringify(TimerMaps))
}

export function getGamemode(player) {
    return Object.values(GameMode).find((g) => [...world.getPlayers({ name: player.id, gameMode: g })].length);
}


export function entityInView(player, distance, angle) {
    let targets = []
    const viewDir = player.getViewDirection();
    const playerLocation = player.getHeadLocation();
    //Idk exact values for filter here
    player.dimension.getEntities({
        location: playerLocation,
        type: 'minecraft:player',
        minDistance: 2
    }).forEach(entity => {
        //May want to adjust to middle of body for better accuracy
        const entityLocation = entity.location;
        const distance = { x: entityLocation.x - playerLocation.x, y: entityLocation.y - playerLocation.y, z: entityLocation.z - playerLocation.z };
        const magnitude = Math.sqrt(distance.x ** 2 + distance.y ** 2 + distance.z ** 2);
        const dotProduct = Math.acos((viewDir.x * distance.x + viewDir.y * distance.y + viewDir.z * distance.z) / magnitude);
        if (dotProduct >= angle * (Math.PI / 180)) {
            targets.push(entity)
        }
    });
};

export function getAmmoItem(player, ammos) {
    const container = player.getComponent('inventory').container
    if (container.emptySlotsCount !== container.size){
        for (let i = 0; i < container.size; i++){
            const item = container.getItem(i);
                            
            if (item !== undefined) {
                const itemId = item.typeId;
                                
                if(itemId == ammos) {
                    return item
                }
            }
        }
    }
}

export function getAmmoCount(player, ammos) {
    let ammoCount = 0
    const container = player.getComponent('inventory').container
    if (container.emptySlotsCount !== container.size){
        for (let i = 0; i < container.size; i++){
            const item = container.getItem(i);
                            
            if (item !== undefined) {
                const itemId = item.typeId;
                                
                if(itemId == ammos) {
                    ammoCount = ammoCount + item.amount
                }
            }
        }
    }
    return ammoCount
}

export function getPotionItem(player, item) {
    let name = ""
    let pushed = []
    const container = player.getComponent('inventory').container
    if (container.emptySlotsCount !== container.size){
        for (let i = 0; i < container.size; i++){
            const item = container.getItem(i);
                            
            if (item !== undefined) {
                const itemId = item.typeId;
                                
                if(itemId == item && item.name != "Potion of Turtle Master") {
                    name = item.name.replace("Potion of ", "")
                    pushed.push(name.toLowerCase())
                } else if(itemId == item && item.name == "Potion of Turtle Master") {
                    pushed.push("slowness")
                    pushed.push("strength")
                }
            }
        }
    }
    return pushed
}

export function getDistance(player, entity){
   const {x, y, z} = entity.location
    let d_x = x - player.location.x
    let d_y = y - player.location.y
    let d_z = z - player.location.z
    let distance = Math.sqrt(d_x ** 2 + d_y ** 2 + d_z ** 2);
    let Distance = Math.floor(distance);
    return Distance;
}

export function getDistanceY(player, entity){
      let d_y = entity.location.y - player.location.y
      let distance = Math.sqrt(d_y ** 2);
      let Distance = Math.floor(distance);
      return Distance;
}

export function backShotCheck(player, entity) {
    let angle = player.getRotation().y - entity.getRotation().y
    let shot = false
    angle < 90 && angle > -90 ? shot = true : shot = shot
    return shot
}

export function weaponDamageIndicator(item) {
    let geezer = 2
    let itemer
    if (!item) {
        return geezer
    }
    try {
        geezer = ModifiedWeapons.get(item.typeId).damage
        return geezer
    } catch (error) {

    }
    try {
        itemer = item?.getTags()
    } catch (error) {

    }
    try {
        for (let i = 0; i < itemer.length; i++) {
            let neededTag = itemer[i].split(":")
            if (neededTag[0] == "damage") {
                geezer = neededTag[1]
            } else if (i != (itemer.length - 1)) {
                continue;
            } else geezer = geezer
        }
    } catch (err) {
        geezer = 2

    }
    return geezer
}


export function stunDamageIndicator(item) {
    try{
        for(let i = 0; i < item.length; i++){
            let neededTag = item[i].split(":")
            if(neededTag[0] == "stun_damage") {
                let geezer = neededTag[1]
                return geezer
            } else if(i != (item.length - 1)) {
                continue;
            } else return 0
        }
    } catch(err) {
        return 0
    }
}

export function weaponTypeSplicer(item) {
    let geezer = "unarmed"
    let itemer
    try {
        itemer = item?.getTags()
    } catch (error) {
        return "unarmed"
    }
    let ids = ["shield","longsword","greatsword","sword","axe","mace","katana","dagger","halberd","gauntlet", "glaive", "grand_cross","katar", "hand_cannon", "lance", "odachi", "pistol", "polehammer", "rapier", "saber", "scythe", "spear", "war_hammer"]
    for(let i = 0; i < ids.length; i++) {
        let petals = item?.typeId?.includes(ids[i])
        petals ? geezer = ids[i] : geezer = "unarmed"
        if(geezer != "unarmed") {
            return geezer
        }
    }
    try{
        for(let i = 0; i < itemer.length; i++){
            let neededTag = itemer[i].split(":")
            if(neededTag[0] == "weaponType") {
                geezer = neededTag[1].replace("is_", "") 
            } else if(i != (itemer.length - 1)) {
                continue;
            } else geezer = geezer
        }
    } catch(err) {
        geezer = "unarmed"
        
    }
    return geezer
}

export function stunLock(entity, ticks) {
    entity.addEffect("slowness", ticks, { amplifier: 3, showParticle: false })
    entity.addEffect("weakness", ticks, { amplifier: 100, showParticle: false })
    if (entity.typeId == "minecraft:player"){
        entity.runCommandAsync("execute as @s run scoreboard players set @s stunned 1");
        entity.setDynamicProperty("attacking",0)
        const lmaobruha = system.runInterval(() => {
            entity.runCommandAsync("execute as @s run scoreboard players set @s stunned 0");
            entity.removeTag("attack")
            system.clearRun(lmaobruha);
        },ticks);
    }
}

export function levelGetter(item) {
    for(let i = 0; i < item.length; i++){
        let neededTag = item[i].split(":")
        if(neededTag[0] == "lvl") {
            let geezer = neededTag[1]
            return geezer
        } else if(i != (item.length - 1)) {
            continue;
        } else return "unidentified"
    }
}

export function coordExtractor(player, yAdditive) {
    const {x, y , z} = player.location
    let yg = y + yAdditive
    return new Vector3(x,yg,z);
}

export function NTS(player, face, dist) {
    let targetsList = []
    let checker = system.runInterval(()=>{
        let tgt = player.getEntitiesFromViewDirection({maxDistance:dist,}).filter((hit) => hit.entity.typeId != "pixel:hit")[0].entity;
        for(let i = 0; i < (tgt.length - 1); i++) {
            if(!targetsList.includes(tgt[i])) {
                targetsList.push(tgt[i])
                
            }
        }
        world.afterEvents.itemStopUse.subscribe((data) => {
            system.clearRun(checker)
        })
    })
    return targetsList;
}

/*export function enemyArmorCalculator(enemy, Map) {
      let eHelm
      let eChest
      let eLegs
      let eBoots
      
      let helm = enemy.getComponent('equippable').getEquipment('Head')
      
     helm != undefined ? eHelm = enemyDefense.get(helm.typeId.split(':')[1]) : eHelm = 0
     
      let chest = enemy.getComponent('equippable').getEquipment('Chest')
      
     chest != undefined ? eChest = enemyDefense.get(chest.typeId.split(':')[1]) : eChest = 0
      
      let legs = enemy.getComponent('equippable').getEquipment('Legs')
      
     legs != undefined ? eLegs = enemyDefense.get(legs.typeId.split(':')[1]) : eLegs = 0
      
      let feet = enemy.getComponent('equippable').getEquipment('Feet')
      
     feet != undefined ? eBoots = enemyDefense.get(feet.typeId.split(':')[1]) : eBoots = 0
     
    let calcs = parseFloat((eHelm + eChest + eLegs + eBoots) / 2)
    return calcs
}*/

export function trailMaker(player, entity, y, distance) {
    let loc = coordExtractor(player, y)
    let frontLoc = Vector3.Add(loc, Vector3.Scale(player.getViewDirection(), distance));
    const proj = player.dimension.spawnEntity(entity, frontLoc)
    player.runCommandAsync("execute as @s run tp @e[type=pixel:blunt_trail,c=1,r=3] ^ ^1 ^ ~ ~")
    player.runCommandAsync("execute as @e[type=pixel:blunt_trail,c=1,r=3] run playanimation @s animation.blunt_trail.right")
}
