import { system, world, ItemStack, Player } from "@minecraft/server";

import {
    notificationMessage,
    modifySpawnLocationFromBlockFace,
    applyFacingRotation,
    applyFacingRotationNormal,
    removeHeldItem
} from "./common";

import {
    EXCLUDED_INTERACTION_BLOCKS,
    BREAK_PUNCH_WINDOW_TICKS,
    MAX_BREAK_PUNCHES,
    colorCodeIds,
    spawnItems,
    entityBreak,
    getVehicleStats
} from "./spcrs_data.js";

import {
    checkRiddenCars,
    runCarLogic,
    updatePlayerRidingStatus,
    toggleOrbitCamera
} from "./driving";

import {
    openGuidebookMenu
} from "./forms";

// defines the name of the Dynamic Property for storing if the guidebook was given.
const givenBookString = "oct_spcrs:givenBook";
// state flags
let showedForm = false;
let spawnEntityFlag = false;

// Interval logic
system.runInterval(() => {
    if (system.currentTick % 2 === 0) {
        try {
            updatePlayerRidingStatus();
        } catch (error) {
            console.error(`§cERROR: updatePlayerRidingStatus failed\n${error}`);
        }
    }
    if (system.currentTick % 10 === 0) {
        try {
            checkRiddenCars();
        } catch (error) {
            console.error(`§cERROR: checkRiddenCars failed\n${error}`);
        }
    }
    // Run logic on each driven car
    runCarLogic();

}, 1);

// Listen for /scriptevent commands
system.afterEvents.scriptEventReceive.subscribe((event) => {
    const { id, message, sourceEntity } = event;

    // EVENTS: Eggs
    if (id == 'oct_spcrs:drop_egg' && message == "manual_egg") {
        // Spawn the manual egg item
        entityBreak["oct_spcrs:car_manual"].onBreakFunction(sourceEntity);
    }
    // EVENTS: Cars
    if (id == 'oct_spcrs:car') {
        switch (message) {
            case "has_rider":
                // world.playSound('octovon.shpao.delivery_truck.engine.start', sourceEntity.location);
                break;
            case "no_rider":
                break;
            case "riders_left":
                const soundKeys = getVehicleStats(sourceEntity?.typeId).soundKeys;
                world.playSound(soundKeys.engineStop, sourceEntity.location);
                world.playSound(soundKeys.doorClose, sourceEntity.location);

                sourceEntity.setDynamicProperty("oct_spcrs:is_boosting", false);
                sourceEntity.setDynamicProperty("oct_spcrs:carState", "idle");

                sourceEntity.setProperty("oct_spcrs:anim.nitro", false);
                sourceEntity.setProperty("oct_spcrs:anim.speed_lines", false);
                sourceEntity.setProperty("oct_spcrs:anim.break", true);
                // sourceEntity.setDynamicProperty("oct_spcrs:prevSpeed", 0);

                sourceEntity.getComponent("minecraft:movement").resetToDefaultValue();

                break;
            default:
                break;
        }
        return;
    };

});


// Entity Loading
world.afterEvents.playerSpawn.subscribe(event => {
    const { player } = event;
    if (!player.isValid()) return;

    if (event.initialSpawn) {
        checkForGuidebook(player);
    }
    // Reset orbit camera if the loaded player had it active and is still riding something
    const ridingComp = player.getComponent("minecraft:riding");
    if (player.getDynamicProperty('oct_spcrs:follow_orbit_camera')
        && ridingComp !== undefined
        && ridingComp?.entityRidingOn !== undefined) {

        player.camera.setCamera("minecraft:follow_orbit");
    }
});
function checkForGuidebook(player) {
    if (!player.getDynamicProperty(givenBookString)) {
        system.runTimeout(() => {
            const guidebook = new ItemStack('oct_spcrs:car_manual', 1);
            player.dimension.spawnItem(guidebook, player.location);
            const msg = {
                rawtext: [
                    {
                        "translate": "message.oct_spcrs.first_load", "with": ["\n", `${player.name}`]
                    }
                ]
            };
            notificationMessage(msg, player);
            player.setDynamicProperty(givenBookString, true);
        }, 60)
    }
}

// Item Interactions
world.beforeEvents.itemUse.subscribe(event => {
    if (event.itemStack?.typeId === "oct_spcrs:car_manual" && event.source.isSneaking) {
        event.cancel = true;
        if (!showedForm) {
            showedForm = true;
            system.runTimeout(() => {
                openGuidebookMenu(event.source);
                showedForm = false;
            }, 1);
        }
    }
});
world.beforeEvents.itemUseOn.subscribe((event) => {
    const { source, block, blockFace, itemStack } = event;

    // Check if the event source is a player
    if (!(source instanceof Player)) return;

    // Check if the block type is in the excluded blocks array
    if (EXCLUDED_INTERACTION_BLOCKS.includes(block.typeId)) {
        return;
    }

    // Check if the item type is a custom car item
    if (itemStack.typeId.startsWith("oct_spcrs:car_") && itemStack.typeId !== "oct_spcrs:car_manual") {

        // Prevent multiple spawning with a flag
        if (!spawnEntityFlag) {
            // Set the flag to prevent multiple spawns
            spawnEntityFlag = true;

            let itemStackKey = itemStack.typeId;

            // Check if the itemStack type contains a color code from colorCodeIds
            for (let color in colorCodeIds) {
                if (itemStack.typeId.includes(color)) {
                    // Replace the color code in the item type ID with "COLORCODE"
                    itemStackKey = itemStack.typeId.replace(color, "COLORCODE");
                    break;
                }
            }

            // If the item is defined the spawnItems dictionary
            if (spawnItems[itemStackKey]) {
                // Cancel the event
                event.cancel = true;

                // Spawn the item
                system.run(() => {
                    spawnEntityFromItem(source, block, blockFace, itemStack, spawnItems[itemStackKey]);
                });
            }

            // Set a reset flag cooldown after spawning
            system.runTimeout(() => { spawnEntityFlag = false; }, 5);
        }
    } else if (itemStack.typeId === "oct_spcrs:car_manual" && !source.isSneaking) {

        // Prevent multiple spawning with a flag
        if (!spawnEntityFlag) {
            // Set the flag to prevent multiple spawns
            spawnEntityFlag = true;

            // If the item is defined the spawnItems dictionary
            if (spawnItems[itemStack.typeId]) {
                // Cancel the event
                event.cancel = true;
                // Spawn the item
                system.run(() => {
                    spawnEntityFromItem(source, block, blockFace, itemStack, spawnItems[itemStack.typeId]);
                });
            }

            // Set a reset flag cooldown after spawning
            system.runTimeout(() => { spawnEntityFlag = false; }, 5);
        }
    }

});
function spawnEntityFromItem(source, block, blockFace, itemStack, itemData = undefined) {
    // Get the spawn item data
    let entityId, facingDirections, onSpawnFunction;

    // Get the desired item data
    if (itemData) {
        // Destructure from itemData if provided
        ({ entityId, facingDirections, onSpawnFunction } = itemData);
    } else {
        // Destructure from spawnItems if itemData is not provided
        const spawnItemData = spawnItems[itemStack.typeId];

        if (spawnItemData) {
            ({ entityId, facingDirections, onSpawnFunction } = spawnItemData);
        } else {
            console.error("§cERROR: Invalid item type or spawn data not found.");
            return;
        }
    }

    // Get the desired spawn location
    let spawnLocation = { x: block.location.x + 0.5, y: block.location.y, z: block.location.z + 0.5 };
    spawnLocation = modifySpawnLocationFromBlockFace(spawnLocation, blockFace);

    // Cancel if there are other of the same entities on that block
    const entitiesAtLocation = source.dimension.getEntitiesAtBlockLocation(spawnLocation);
    let entityFound = false;
    entitiesAtLocation.forEach(entity => {
        if (entity.typeId === entityId) {
            entityFound = true;
        }
    });
    if (entityFound) { return; }


    // Spawn the entity
    const spawnedEntity = source.dimension.spawnEntity(entityId, spawnLocation);
    if (!spawnedEntity) {
        // Exit if no entity was spawned
        return;
    }

    // Apply visual rotation if the entity has facingDirections
    if (facingDirections) {
        if (facingDirections > 0 && spawnedEntity.getProperty('oct_spcrs:rotation') !== undefined) {
            applyFacingRotation(source, spawnedEntity, spawnLocation, facingDirections);
        } else {
            // Set the rotation normaly
            applyFacingRotationNormal(source, spawnedEntity, spawnLocation, facingDirections, 90);
        }
    }

    // Run spawn definition event
    if (onSpawnFunction) {
        onSpawnFunction(source, spawnedEntity, spawnLocation, itemStack);
    }

    // Play the place sound
    if (itemData) {
        const spawnSound = itemData.spawnSound ?? null;
        if (spawnSound) {
            world.playSound(spawnSound.sound, block.location);
        }
    } else {
        const spawnSound = spawnItems[itemStack.typeId].spawnSound ?? null;
        if (spawnSound) {
            world.playSound(spawnSound.sound, block.location);
        }
    }

    // Delete the spawn egg item from the player's hand
    removeHeldItem(source);
}

// Entity Interactions
world.beforeEvents.playerInteractWithEntity.subscribe(event => {
    const { player, target, itemStack } = event;

    // Check if the interaction is with a car
    if (target.typeId.startsWith("oct_spcrs:car_") && target.typeId !== "oct_spcrs:car_manual") {
        const car = target;
        // console.error(`§3DEBUG: ${player.name} interacted with §6${target.typeId}`);

        // Cancel this logic if the player is sneaking
        if (player.isSneaking) {
            return;
        }

        // cancel the vanilla ride event
        event.cancel = true;

        // Car Camera interaction if already in a car
        if (player.getDynamicProperty('oct_spcrs:is_riding_car')) {
            system.run(() => { // Use system.run() to access privileges
                toggleOrbitCamera(player);
                player.playSound('random.click');
            });
            return;
        }

        // Paint Interaction
        if (itemStack && itemStack?.typeId.startsWith("oct_spcrs:car_paint_")) {
            //console.info(`§3DEBUG: ${player.name} is holding a paint.`);
            const paintColor = itemStack.typeId.replace("oct_spcrs:car_paint_", "");
            const colorId = colorCodeIds[paintColor] ?? 6;
            const currentCarColorId = car.getProperty("oct_spcrs:car_color_id");

            if (currentCarColorId !== colorId && currentCarColorId !== 8 /* Police vehicle */) {
                system.run(() => { // Use system.run() to access privileges
                    // Apply the new color
                    car.setProperty("oct_spcrs:car_color_id", colorId);

                    // Remove the paint from the player's hand
                    removeHeldItem(player);

                    // Play the paint sound
                    world.playSound('oct_spcrs.car.paint', car.location);
                    car.dimension.spawnParticle(`oct_spcrs:paint_${paintColor}`, car.location);
                });
                //console.info(`§3DEBUG: Car color changed to ${paintColor}.`);
                return;
            };
        }

        // Fuel Interaction
        const validCannisters = {
            gas: "oct_spcrs:gas_cannister_filled",
            electric: "oct_spcrs:power_bank_filled",
            diesel: "oct_spcrs:diesel_cannister_filled"
        };

        if (itemStack && Object.values(validCannisters).includes(itemStack.typeId)) {
            const fuelType = getVehicleStats(car.typeId)?.fuelType ?? "gas";
            const expectedCannister = validCannisters[fuelType];
            const fuelNames = {
                gas: 'Gasoline Cannister',
                electric: 'Power Bank',
                diesel: 'Diesel Cannister'
            };

            if (itemStack.typeId !== expectedCannister) {
                const errMessage = { rawtext: [{ "translate": "message.oct_spcrs.wrong_cannister", "with": [`${fuelNames[fuelType]}`] }] };
                world.sendMessage(errMessage);
                return;
            }

            let currentCarFuel = car.getDynamicProperty("oct_spcrs:fuel_level") ?? 0;
            if (currentCarFuel < 500) {

                system.run(() => { // Use system.run() to access privileges
                    // world.sendMessage('§c-----------OH HI MARK');
                    const Cannister = itemStack.getComponent("minecraft:durability");
                    const CurrentCannisterFuel = Cannister.maxDurability - Cannister.damage;

                    // console.info(`§3DEBUG: Car Tank: ${currentCarFuel} / 500`);
                    // console.info(`§3DEBUG: Fuel Cannister: ${CurrentCannisterFuel} / ${Cannister.maxDurability}`);

                    // Fill the car with the remaining fuel
                    const addedCarFuel = Math.min(500 - currentCarFuel, CurrentCannisterFuel);
                    currentCarFuel += addedCarFuel
                    const currentCarFuelPercent = Math.round(currentCarFuel / 500 * 100);
                    car.setDynamicProperty("oct_spcrs:fuel_level", currentCarFuel);

                    // Calculate remaining fuel in the cannister
                    const remainingCannisterFuel = CurrentCannisterFuel - addedCarFuel;
                    // console.info(`§cDEBUG: New Car Tank: ${currentCarFuel} / 500`);
                    // console.info(`§cDEBUG: Remaining Cannister Fuel: ${remainingCannisterFuel} / ${Cannister.maxDurability}`);

                    // Replace the cannister with the new one
                    if (remainingCannisterFuel > 0) {
                        const newCannister = new ItemStack(itemStack.typeId, 1);
                        newCannister.getComponent("minecraft:durability").damage = Cannister.maxDurability - remainingCannisterFuel;
                        player.getComponent("inventory").container.setItem(player.selectedSlotIndex, newCannister);

                        world.sendMessage({ rawtext: [{ "translate": "message.oct_spcrs.tank_filled", "with": [`${currentCarFuelPercent}`] }] });

                    } else {
                        const newEmptyCannister = new ItemStack(itemStack.typeId.replace("filled", "empty"), 1);
                        player.getComponent("inventory").container.setItem(player.selectedSlotIndex, newEmptyCannister);

                        world.sendMessage({ rawtext: [{ "translate": "message.oct_spcrs.tank_filled", "with": [`${currentCarFuelPercent}`] }] });
                        world.sendMessage({ rawtext: [{ "translate": "message.oct_spcrs.cannister_empty", "with": [`${fuelNames[fuelType]}`] }] });
                    }

                    // Play the fuel sound
                    switch (fuelType) {
                        case "gas":
                            world.playSound('oct_spcrs.car.gas_fill', car.location);
                            break;
                        case "electric":
                            world.playSound('oct_spcrs.car.electric_fill', car.location);
                            break;
                    }

                });
            } else {
                world.sendMessage({ rawtext: [{ "translate": "message.oct_spcrs.tank_full", "with": [`\n`] }] });
            }

            return;
        }

        // Get the rideable component
        const rideableComp = car.getComponent("minecraft:rideable");
        const riders = rideableComp.getRiders() ?? [];
        const seatCount = rideableComp.seatCount;

        // Check if there's an available seat
        if (riders.length < seatCount) {
            system.run(() => { // Use system.run() to access privileges
                const soundKeys = getVehicleStats(car?.typeId).soundKeys;
                //console.info(`${car?.typeId}`);

                // Trigger the engine start sound on the first player added
                if (riders.length === 0) {
                    world.playSound(soundKeys.engineStart, car.location);
                }

                // Attempt to add the player as a rider
                const added = rideableComp.addRider(player);
                if (added) {
                    const playerSetting = player.getDynamicProperty('oct_spcrs:setting.disable_car_camera');
                    if (!playerSetting) {
                        toggleOrbitCamera(player);
                    }

                    player.setDynamicProperty('oct_spcrs:is_riding_car', true);

                } else {
                    //world.sendMessage("§cERROR: Unable to add rider.");
                    return;
                }

            });
        }
        else {
            // world.sendMessage("§cERROR: No available seats.");
            return;
        }
    }
});
world.afterEvents.entityHitEntity.subscribe((event) => {
    const { damagingEntity, hitEntity } = event;

    if (damagingEntity instanceof Player) {

        // Car horn interaction
        if (damagingEntity.getDynamicProperty('oct_spcrs:is_riding_car')) {
            if (damagingEntity.getComponent("minecraft:riding").entityRidingOn === hitEntity) {
                const isPoliceCar = hitEntity?.getProperty("oct_spcrs:car_color_id") === 8;
                const hornSoundKey = isPoliceCar ?
                    getVehicleStats(hitEntity?.typeId).soundKeys.siren
                    : getVehicleStats(hitEntity?.typeId).soundKeys.horn;

                world.playSound(hornSoundKey, hitEntity.location, { volume: 1.0, pitch: 1.0 });
            }
            event.cancel = true;
            return;
        }

        // Breakable entities
        if (hitEntity.getComponent("type_family").hasTypeFamily('breakable_by_punching')) {
            let canBreakEntity = true; // Flag to determine if the entity can be broken

            if (hitEntity.typeId.startsWith('oct_spcrs:car_')) {
                // ...
            }

            // Only call breakableEntity if the entity can be broken
            if (canBreakEntity) {
                breakableEntity(damagingEntity, hitEntity);
            }

            return;
        }

    }
});
world.beforeEvents.playerBreakBlock.subscribe(event => {
    if (event.player.getDynamicProperty('oct_spcrs:is_riding_car')) {
        event.cancel = true;
        return;
    }
});
const rubberTrees = [
    'minecraft:stripped_oak_log',
    'minecraft:stripped_oak_wood',
    'minecraft:stripped_jungle_log',
    'minecraft:stripped_jungle_wood'
];
world.afterEvents.itemUseOn.subscribe((eventData) => {
    const { source, block, itemStack, blockFace } = eventData;
    //console.warn(block.typeId, JSON.stringify(block.permutation.getTags()));

    const rubberSetting = world.getDynamicProperty('oct_spcrs:setting.disable_rubber');
    if (!rubberSetting && block.hasTag('wood') && rubberTrees.includes(block.typeId) && itemStack.typeId.startsWith('minecraft:') && itemStack.typeId.endsWith('_axe')) {
        const randomRoll = Math.random();
        const dropChance = 0.25;
        if (randomRoll > dropChance) {
            return;
        }
        //world.sendMessage(`§2${source.name} used item ${itemStack.typeId} on block at ${x}, ${y}, ${z}`);
        const spawnLoc = modifySpawnLocationFromBlockFace(
            {
                x: block.location.x + 0.5,
                y: block.location.y,
                z: block.location.z + 0.5
            },
            blockFace);
        const rubberItem = new ItemStack('oct_spcrs:natural_rubber', 1);
        source.dimension.spawnItem(rubberItem, spawnLoc);
    }
});
function breakableEntity(damagingEntity, hitEntity) {
    // Retrieve or initialize punch data
    let punchCount = hitEntity.getDynamicProperty("oct_spcrs:punchCount") || 0;
    let firstPunchTick = hitEntity.getDynamicProperty("oct_spcrs:firstPunchTick") || 0;

    // Get current game tick
    const currentTick = system.currentTick;

    // Check if we need to reset punch data (if punch is outside time window)
    if (currentTick - firstPunchTick > BREAK_PUNCH_WINDOW_TICKS) {
        punchCount = 1; // Reset to 1 punch
        firstPunchTick = currentTick; // Update the first punch tick to the current tick
    } else {
        punchCount += 1; // Increment the punch count
    }

    // Store the updated punch count and tick as dynamic properties
    hitEntity.setDynamicProperty("oct_spcrs:punchCount", punchCount);
    hitEntity.setDynamicProperty("oct_spcrs:firstPunchTick", firstPunchTick);

    // Trigger punch animation
    hitEntity.triggerEvent('oct_spcrs:is_punched');

    // Play the hit sound
    const hitSound = entityBreak[hitEntity.typeId].hitSound ?? null;
    if (hitSound) {
        const randomPitch = hitSound.pitchMin + (Math.random() * (hitSound.pitchMax - hitSound.pitchMin));
        world.playSound(hitSound.sound, hitEntity.location, { pitch: randomPitch });
    }

    // Check if the punch count has reached the maximum punches
    if (punchCount >= MAX_BREAK_PUNCHES) {
        // Reset punch data
        hitEntity.setDynamicProperty("oct_spcrs:punchCount", 0); // Reset punch count
        hitEntity.setDynamicProperty("oct_spcrs:firstPunchTick", 0); // Reset the punch timer

        // Trigger the event after X consecutive punches
        breakEntityAfterPunching(hitEntity);
    }
}
function breakEntityAfterPunching(entity) {
    // Check the entity type in the entityBreak dictionary
    const breakData = entityBreak[entity.typeId];

    // Spawn the spawn item if it has one
    try {
        if (breakData && breakData.spawnItem) {
            const itemStack = new ItemStack(breakData.spawnItem, 1);
            entity.dimension.spawnItem(itemStack, entity.location);
        }

        // Run onBreak logic if defined
        if (breakData && breakData.onBreakFunction) {
            breakData.onBreakFunction(entity);
        }

        // Play the break sound
        const breakSound = entityBreak[entity.typeId].breakSound ?? null;
        if (breakSound) {
            world.playSound(breakSound.sound, entity.location);
        }
    } catch (error) { }

    // Despawn the entity
    entity.remove();
}