import { BlockComponentTypes, EntityComponentTypes, ItemStack, system, world } from "@minecraft/server";
const PREFIX = "";
const TIMER_PREFIX = PREFIX + "§,";
const TICK_PREFIX = PREFIX + "§.";
const UPDATE_PREFIX = PREFIX + "§/";
var SpoilSpeed;
(function (SpoilSpeed) {
    SpoilSpeed[SpoilSpeed["SLOW"] = 72000] = "SLOW";
    SpoilSpeed[SpoilSpeed["MEDIUM"] = 48000] = "MEDIUM";
    SpoilSpeed[SpoilSpeed["FAST"] = 36000] = "FAST";
    SpoilSpeed[SpoilSpeed["VERY_FAST"] = 24000] = "VERY_FAST";
    SpoilSpeed[SpoilSpeed["DEBUG"] = 200] = "DEBUG";
})(SpoilSpeed || (SpoilSpeed = {}));
;
export const SPOIL_UPDATE_INTERVAL = 1200;
function getSpoilSpeed() {
    // Difficulty modifiers
    const difficulty = world.getDynamicProperty('oct_db:worldSetting');
    if (difficulty === 1) {
        return {
            SLOW: SpoilSpeed.SLOW * 2,
            MEDIUM: SpoilSpeed.MEDIUM * 2,
            FAST: SpoilSpeed.FAST * 2,
            VERY_FAST: SpoilSpeed.VERY_FAST * 2,
            DEBUG: SpoilSpeed.DEBUG * 2
        };
    }
    else if (difficulty === 2) {
        return {
            SLOW: SpoilSpeed.SLOW * 1.5,
            MEDIUM: SpoilSpeed.MEDIUM * 1.5,
            FAST: SpoilSpeed.FAST * 1.5,
            VERY_FAST: SpoilSpeed.VERY_FAST * 1.5,
            DEBUG: SpoilSpeed.DEBUG * 1.5
        };
    }
    else {
        return {
            SLOW: SpoilSpeed.SLOW,
            MEDIUM: SpoilSpeed.MEDIUM,
            FAST: SpoilSpeed.FAST,
            VERY_FAST: SpoilSpeed.VERY_FAST,
            DEBUG: SpoilSpeed.DEBUG
        };
    }
}
const SpoiledFood = [
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:bread", "oct_db:stale_bread", "oct_db:moldy_bread"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:pumpkin_pie", "oct_db:stale_pumpkin_pie", "oct_db:moldy_pumpkin_pie"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:baked_potato", "oct_db:stale_baked_potato", "oct_db:moldy_baked_potato"], },
    { spoilSpeed: getSpoilSpeed().FAST, states: ["minecraft:cookie", "oct_db:stale_cookie", "oct_db:moldy_cookie"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_beef", "oct_db:stale_steak", "oct_db:moldy_steak"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_porkchop", "oct_db:stale_cooked_porkchop", "oct_db:moldy_cooked_porkchop"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_mutton", "oct_db:stale_cooked_mutton", "oct_db:moldy_cooked_mutton"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_chicken", "oct_db:stale_cooked_chicken", "oct_db:moldy_cooked_chicken"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_rabbit", "oct_db:stale_cooked_rabbit", "oct_db:moldy_cooked_rabbit"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_cod", "oct_db:stale_cooked_cod", "oct_db:moldy_cooked_cod"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:cooked_salmon", "oct_db:stale_cooked_salmon", "oct_db:moldy_cooked_salmon"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:rabbit_stew", "oct_db:stale_rabbit_stew", "oct_db:moldy_rabbit_stew"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:mushroom_stew", "oct_db:stale_mushroom_stew", "oct_db:moldy_mushroom_stew"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:beetroot_soup", "oct_db:stale_beetroot_soup", "oct_db:moldy_beetroot_soup"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:apple", "oct_db:rotten_apple"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:beetroot", "oct_db:rotten_beetroot"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:carrot", "oct_db:rotten_carrot"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:melon_slice", "oct_db:rotten_watermelon"], },
    { spoilSpeed: getSpoilSpeed().SLOW, states: ["minecraft:potato", "oct_db:rotten_potato"], },
    { spoilSpeed: getSpoilSpeed().SLOW, states: ["minecraft:poisonous_potato", "oct_db:extra_poisonous_potato"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:sweet_berries", "oct_db:rotten_sweet_berries"], },
    { spoilSpeed: getSpoilSpeed().MEDIUM, states: ["minecraft:glow_berries", "oct_db:rotten_glow_berries"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:beef", "oct_db:rotten_beef"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:porkchop", "oct_db:rotten_porkchop"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:mutton", "oct_db:rotten_mutton"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:chicken", "oct_db:rotten_chicken"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:rabbit", "oct_db:rotten_rabbit"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:cod", "oct_db:rotten_cod"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:salmon", "oct_db:rotten_salmon"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:tropical_fish", "oct_db:rotten_tropical_fish"], },
    { spoilSpeed: getSpoilSpeed().VERY_FAST, states: ["minecraft:pufferfish", "oct_db:rotten_pufferfish"], },
];
export const FoodSpoilageMethods = {
    updateFoodSpoilage: function (invenComp) {
        const foodDataArr = this.getFoodItems(invenComp);
        this.updateFoodTimers(foodDataArr, invenComp);
        this.updateFoodStates(foodDataArr, invenComp);
    },
    updateFoodStates: function (foodDataArr, invenComp) {
        for (let i = 0; i < foodDataArr.length; i++) {
            const foodData = foodDataArr[i];
            if (foodData.initTick === undefined)
                continue;
            const item = invenComp.container.getItem(foodData.slotNumber);
            // Get spoil progress
            const spoilProgress = Math.min(1, 1 - ((foodData.spoilSpeed - (system.currentTick - foodData.initTick)) / foodData.spoilSpeed));
            const numStates = foodData.states.length;
            let stateIndex;
            if (spoilProgress >= 1)
                stateIndex = numStates - 1; // Fully spoiled state
            else {
                const segmentSize = 1 / (numStates - 1);
                stateIndex = Math.max(0, Math.floor(spoilProgress / segmentSize));
            }
            // Do checks
            if (foodData.states[stateIndex] === undefined)
                continue;
            if (foodData.states[stateIndex] === item.typeId)
                continue;
            // Set new item
            const newItem = new ItemStack(foodData.states[stateIndex], item.amount);
            newItem.setLore(item.getLore());
            newItem.nameTag = item.nameTag;
            invenComp.container.setItem(foodData.slotNumber, newItem);
        }
    },
    updateFoodTimers: function (foodDataArr, invenComp) {
        // Check if the Preservation Talisman is in the inventory
        const hasTalisman = Array.from({ length: invenComp.container.size }, (_, i) => invenComp.container.getItem(i)).some(item => item?.typeId === "oct_db:preservation_talisman");
        // Check if block inventory or none player inventory has blue ice
        /**
         * This is not going to be the best way to do this, but it will work for now
         * I'm going to determine whether it's a none player inventory based on the number of inventory slots. Player has 36
         */
        const hasBlueIce = Array.from({ length: invenComp.container.size }, (_, i) => invenComp.container.getItem(i)).some(item => item?.typeId === "minecraft:blue_ice");
        const blueIceFactor = 0.25;
        for (let i = 0; i < foodDataArr.length; i++) {
            const foodData = foodDataArr[i];
            const item = invenComp.container.getItem(foodData.slotNumber);
            if (!item)
                continue; // Skip empty slots
            // If spoiled already, skip
            if (item.getLore().find(l => l.includes(TIMER_PREFIX))?.includes("Spoiled"))
                continue;
            // If player has preservation talisman, increment spoil
            if (hasTalisman) {
                foodData.initTick += SPOIL_UPDATE_INTERVAL;
            }
            if (!hasTalisman && hasBlueIce) {
                foodData.initTick += Math.floor((system.currentTick - foodData.lastUpdate) * blueIceFactor);
                // console.warn(`Current tick: ${system.currentTick}, Last update: ${foodData.lastUpdate}, Init tick: ${foodData.initTick}`);
            }
            // Format tick count for lore
            const formatted = (input) => Math.floor(input).toString().split("").map(c => `§${c}`).join("");
            const decayFactor = hasBlueIce ? 1 / blueIceFactor : 1; // Adjust only remaining time
            const timeLeft = Math.max(0, (((foodData.initTick) + foodData.spoilSpeed - system.currentTick) * decayFactor) / 20 / 60);
            const timeString = timeLeft <= 0 ? "§cSpoiled" : `§cSpoils in ${Number.isNaN(timeLeft) ? "-" : timeLeft.toFixed(0)} minutes`;
            // Check and update lore
            let loreArr = item.getLore();
            // Init tick and last update timer combined
            const combinedIndex = loreArr.findIndex(l => l.includes(TICK_PREFIX));
            const combinedLore = `${TICK_PREFIX}${formatted(foodData.initTick)}${UPDATE_PREFIX}${formatted(system.currentTick)}`;
            if (combinedIndex === -1)
                loreArr.push(combinedLore);
            else
                loreArr[combinedIndex] = combinedLore;
            // Spoil timer
            const existingTimeIndex = loreArr.findIndex(l => l.includes(TIMER_PREFIX));
            if (existingTimeIndex === -1)
                loreArr.push(`${TIMER_PREFIX}${timeString}`);
            else
                loreArr[existingTimeIndex] = `${TIMER_PREFIX}${timeString}`;
            item.setLore(loreArr);
            invenComp.container.setItem(foodData.slotNumber, item);
        }
    },
    getFoodItems: function (invenComp) {
        const foodDataArr = [];
        const typeIdData = new Map();
        for (let i = 0; i < invenComp.container.size; i++) {
            const item = invenComp.container.getItem(i);
            if (!item)
                continue; // Skip empty slots
            const spoiledFoodData = SpoiledFood.find(food => food.states.includes(item.typeId));
            if (!spoiledFoodData)
                continue; // Skip non-food items
            const slotNumber = i;
            const lore = item.getLore();
            // Get data from combined lore
            const tickLore = lore.find(l => l.startsWith(TICK_PREFIX))?.split(UPDATE_PREFIX)[0]?.replace(TICK_PREFIX, "").replaceAll("§", "");
            const lastUpdateLore = Number(lore.find(l => l.startsWith(TICK_PREFIX))?.split(UPDATE_PREFIX)[1]?.replace(UPDATE_PREFIX, "").replaceAll("§", "") ?? system.currentTick);
            // Determine initial tick
            const spoiledStateIndex = spoiledFoodData.states.indexOf(item.typeId);
            const numStates = Math.max(1, spoiledFoodData.states.length - 1);
            let initTick = tickLore ? Number(tickLore) : (system.currentTick - (spoiledStateIndex / numStates) * spoiledFoodData.spoilSpeed);
            // Store item data
            foodDataArr.push({
                slotNumber,
                initTick,
                typeId: item.typeId,
                amount: item.amount,
                spoilSpeed: spoiledFoodData.spoilSpeed,
                states: spoiledFoodData.states,
                lastUpdate: lastUpdateLore,
            });
            // Track total ticks and amount for averaging
            if (!typeIdData.has(item.typeId)) {
                typeIdData.set(item.typeId, { totalInitTick: initTick * item.amount, totalAmount: item.amount });
            }
            else {
                const data = typeIdData.get(item.typeId);
                data.totalInitTick += initTick * item.amount;
                data.totalAmount += item.amount;
            }
        }
        // Step 2: Apply averaged `initTick` values to each food item
        for (const food of foodDataArr) {
            const data = typeIdData.get(food.typeId);
            if (data && data.totalAmount > 0) {
                food.initTick = data.totalInitTick / data.totalAmount; // Average initTick for identical items
            }
        }
        return foodDataArr;
    },
};
// Update containers when player interacts
world.afterEvents.playerInteractWithBlock.subscribe(result => {
    const blockInvenComp = result.block.getComponent(BlockComponentTypes.Inventory);
    if (!blockInvenComp)
        return;
    result.player.updateFoodSpoilage(blockInvenComp);
});
world.afterEvents.playerInteractWithEntity.subscribe(result => {
    const entityInvenComp = result.target.getComponent(EntityComponentTypes.Inventory);
    if (!entityInvenComp)
        return;
    result.player.updateFoodSpoilage(entityInvenComp);
});
