import {bookInfo, enchantList, enchantValues, itemList} from "cubc/mo_en/enchantinfo"

export function setScore(entity, scoreboard, value) {
    try {
        entity.runCommand(`scoreboard objectives add ${scoreboard} dummy`);
        entity.runCommand(`scoreboard players set @s ${scoreboard} ${value}`);
    } catch (e) {
        console.warn(e);
    }
}

export function addScore(entity, scoreboard, value) {
    try {
        entity.runCommand(`scoreboard players add @s ${scoreboard} ${value}`);
    } catch (e) {
        console.warn(e);
    }
}

function romanToInt(roman) {
    const romanMap = {
        I: 1,
        V: 5,
        X: 10,
        L: 50,
        C: 100,
        D: 500,
        M: 1000
    };
    let intVal = 0;
    for (let i = 0; i < roman.length; i++) {
        const currentVal = romanMap[roman[i]];
        const nextVal = romanMap[roman[i + 1]];

        if (nextVal && currentVal < nextVal) {
            intVal -= currentVal;
        } else {
            intVal += currentVal;
        }
    }
    return intVal;
}

export function getEnchantLevel(item, enchant) {
    return item.getDynamicProperty(`cubc_mo_en:${enchant}`) ?? 0;
}

export function loreToEnchantList(list) {
    return list.map(item => {
        const enchant = item.slice(0, item.lastIndexOf(' ')).trim();
        const levelRoman = item.slice(item.lastIndexOf(' ') + 1).trim();
        const level = romanToInt(levelRoman);
        return {
            enchant: functional_name(enchant),
            level: level
        };
    });
}

export function enchantListToLore(array) {
    return array.map(item => {
        const levelRoman = intToRoman(parseInt(item.level));
        return `${prettyName(item.enchant)} ${levelRoman}`;
    });
}

export function intToRoman(num) {
    const romanNumerals = [
        ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], ["X", 10],
        ["XL", 40], ["L", 50], ["XC", 90], ["C", 100], ["CD", 400],
        ["D", 500], ["CM", 900], ["M", 1000]
    ];
    let result = '';
    for (let i = romanNumerals.length - 1; i >= 0; i--) {
        while (num >= romanNumerals[i][1]) {
            result += romanNumerals[i][0];
            num -= romanNumerals[i][1];
        }
    }
    return result;
}

export function prettyName(funcName, convertIntsToRoman = false) {
    const replacements = [
        ["minecraft:", ""],
        ["cubc_mo_en:", ""],
        ["cubc_mo_en_", ""],
        ["xp", "XP"],
        ["Xp", "XP"],
    ];

    for (const [old, newStr] of replacements) {
        funcName = funcName.replace(new RegExp(old, 'g'), newStr);
    }

    const words = funcName.replace(/_/g, ' ').split(/\s+/); // Split the input by whitespace
    const titledWords = words.map(word => {
        if (convertIntsToRoman && !isNaN(word)) {
            return intToRoman(parseInt(word));
        }
        return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); // Title-case each word
    });

    return titledWords.join(' ');
}


export function functional_name(name) {
    return name.replace(/\s+/g, '_').replace('-', '_').toLowerCase();
}


export function getCustomEnchants(item) {
    return enchantList.filter(enchant => item.getDynamicProperty(`cubc_mo_en:${enchant}`))
        .map(enchant => ({
            enchant: enchant,
            level: item.getDynamicProperty(`cubc_mo_en:${enchant}`)
        }));
}


export function setCustomEnchants(item, enchantList) {
    for (const enchant of enchantList) {
        item.setDynamicProperty(`cubc_mo_en:${enchant.enchant}`, enchant.level);
    }
}


export function bookApplicable(book_item, item) {
    const allowed_items = bookInfo[book_item.typeId]["items"];

    for (const allowed_item of allowed_items) {
        if (item.typeId.includes(allowed_item)) {
            return true;
        }
    }
    return false;
}


export function applyEnchant(player, existingEnchants, newEnchant, maxEnchantLevel) {
    // Parse the new enchant object
    const {enchant: newEnchantName, level: newEnchantLevel} = newEnchant;

    // Iterate over existing enchants
    for (let enchant of existingEnchants) {
        if (enchant.enchant === newEnchantName) {
            if (enchant.level < newEnchantLevel) {
                enchant.level = newEnchantLevel;
                return true;
            } else if (enchant.level === newEnchantLevel && enchant.level < maxEnchantLevel) {
                enchant.level = newEnchantLevel + 1
                return true;
            } else {
                if (enchant.level === maxEnchantLevel) {
                    player.sendMessage(`§c${prettyName(newEnchantName, true)} is already max level!`)
                } else {
                    player.sendMessage(`§c${prettyName(newEnchantName, true)} can only be upgraded with a higher level book!`)
                }
                // Enchant can not be applied as it is either too low or too high.
                return false;
            }
        }
    }

    // If enchant is not found, add it to the list
    existingEnchants.push({
        enchant: newEnchantName,
        level: newEnchantLevel
    });
    return true;
}

export function isArmor(item) {
    return ["helmet", "chestplate", "leggings", "boots"].some(piece => item.typeId.includes(piece));
}

// Checks which enchants currently apply to the player; and sets them as scoreboard values.
export function applyEnchantScoreboards(player) {
    const equipment = player.getComponent('equippable');
    const equipmentSlots = ['Head', 'Chest', 'Legs', 'Feet', 'Offhand', 'Mainhand'];
    const currentItems = equipmentSlots.map(slot => equipment.getEquipment(slot));

    // Initialize enchantment values
    for (const enchantment of enchantList) {
        setScore(player, enchantment, 0);
    }

    for (let i = 0; i < currentItems.length; i++) {
        const item = currentItems[i];

        // If the item is not enchantable or not present we do not need to check it for enchants
        if (!item || !isEnchantable(item)) {
            continue;
        }

        // Apply the enchant levels
        const enchantments = getCustomEnchants(item);

        // Don't apply enchants if we are holding armor instead of having it equipped.
        if (isArmor(item) && i > 3) {
            continue;
        }

        for (const enchantment of enchantments) {
            addScore(player, enchantment["enchant"], enchantment["level"]);
        }
    }
}

// Gets all the players items
export function getPlayerItems(player) {
    let items = [];

    // Get both the inventory and the equipment slots to enchant
    const container = player.getComponent('inventory')?.container;
    const equipment = player.getComponent('equippable');
    const equipmentSlots = ['Head', 'Chest', 'Legs', 'Feet', 'Offhand'];
    const armors = equipmentSlots.map(slot => equipment.getEquipment(slot));

    for (let i = 0; i < armors.length; i++) {
        items.push({
            "item": armors[i],
            "replaceItemFunction": x => equipment.setEquipment(equipmentSlots[i], x)
        })
    }

    for (let i = 0; i < container.size; i++) {
        const item = container.getItem(i);
        items.push({
            "item": item,
            "replaceItemFunction": x => container.setItem(i, x)
        })
    }

    return items;
}

export function isEnchantable(item) {
    return itemList.some(x => item.typeId.replace('minecraft:', '').includes(x))
}

function randomNumber(min, max) {
    return Math.random() * (max - min) + min;
}


export function getTextureName(item) {
    let item_name = item.replace("golden", "gold").replace('wooden', 'wood');

    if (item.includes('leather')) {
        return `textures/cubc/mo_en/items/leather/${item_name}`
    }

    if (item_name === "bow") {
        return "textures/items/bow_standby";
    }
    if (item_name === "crossbow") {
        return "textures/items/crossbow_arrow";
    }
    return `textures/items/${item_name}`;
}

export function getRandomEnchant(enchantLevel) {
    if (enchantLevel < 1 || enchantLevel > 30) {
        throw new Error("Enchant level must be between 1 and 30");
    }

    // Calculate bounds dynamically based on enchant level
    let lb = Math.max(0, (enchantLevel - 1) / 30); // Normalize to range [0, 1)
    let rb = Math.min(1, enchantLevel / 30);       // Normalize to range [0, 1]

    // Adjust bounds with randomness
    lb += randomNumber(-0.05, 0.05); // Smaller randomness for finer granularity
    rb += randomNumber(-0.05, 0.05);

    // Clamp values between 0 and 1
    lb = Math.max(0, Math.min(1, lb));
    rb = Math.max(0, Math.min(1, rb));

    const possibleEnchants = enchantValues.filter(x => lb <= x["value"] && x["value"] <= rb);

    const enchant = possibleEnchants[Math.floor(Math.random() * possibleEnchants.length)];

    // If no enchant found in the bounds try again
    return enchant ?? getRandomEnchant(enchantLevel);
}
