import { world, system, Entity, ItemStack } from "@minecraft/server"
import { ActionFormData } from '@minecraft/server-ui'

const VALID_ENTITIES = [
    "minecraft:cow",
    "minecraft:sheep",
    "minecraft:goat",
    "minecraft:mooshroom"
]


const ATTRACTION_RADIUS = 6;
const PULL_STRENGTH = 0.1;
const TIME_TO_NEXT_BREEDABLE = 30
const MAX_WHEAT_CAPACITY = 128
const GIVE_UP_TIME = 6;
const GIVE_UP_TIME_COOLDOWN = 10;

class HayGolemMain {
    main(golem) {

        if (golem.getDynamicProperty("lb_ga:hay_golem_animals_breed") == undefined) {
            golem.setDynamicProperty("lb_ga:hay_golem_animals_breed", 0)
        }

        if (golem.getProperty("lb_ga:hay_golem_wheat") >= 2) {
            this.start(golem)
        }
    }
    /**
     * 
     * @param {Entity} golem 
     */
    start(golem) {
        const dimension = golem.dimension;
        const golemPos = golem.location;

        const nearbyEntities = dimension.getEntities({
            excludeTypes: ["lb_ga:hay_golem"],
            excludeTags: ["breedable", "baby_protected", "failed_attempt"]
        }).filter(entity => VALID_ENTITIES.includes(entity.typeId) && this.distance(golemPos, entity.location) <= ATTRACTION_RADIUS)


        const groupedByType = {};
        for (const entity of nearbyEntities) {
            if (!groupedByType[entity.typeId]) {
                groupedByType[entity.typeId] = [];
            }
            groupedByType[entity.typeId].push(entity);
        }

        for (const type in groupedByType) {
            const group = groupedByType[type];

            while (group.length >= 2) {
                const animalA = group.pop();
                const animalB = group.pop();

                if (animalA.hasTag("breedable") || animalB.hasTag("breedable")) continue

                const dist1 = this.distance(golemPos, animalA.location);
                const dist2 = this.distance(golemPos, animalB.location);

                if (dist1 > 1.2 || dist2 > 1.2) {
                    try {
                        this.walkToGolem(animalA, animalB, golem)
                    } catch (e) {
                    }

                } else {
                    this.breedable(animalA, golem)
                    this.breedable(animalB, golem)
                    this.newBabyAnimal(animalA)
                    // increment animals bred only once
                    golem.setDynamicProperty("lb_ga:hay_golem_animals_breed", golem.getDynamicProperty("lb_ga:hay_golem_animals_breed") + 1)

                    golem.setProperty("lb_ga:hay_golem_wheat", golem.getProperty("lb_ga:hay_golem_wheat") - 2)
                    golem.playAnimation("animation.lb_ga_hay_dance")
                    golem.dimension.spawnParticle("lb_ga:circle_particle", golem.location)
                }
            }
        }
    }
    /**
     * 
     * @param {Entity} animalA
     * @param {Entity} animalB
     */
    walkToGolem(animalA, animalB, golem) {
        const golemPos = golem.location;
        const animals = [animalA, animalB];
        const now = Math.floor(Date.now() / 1000);

        animals.forEach((animal) => {
            if (!animal.hasTag("going_to_golem")) {
                animal.addTag("going_to_golem");
                animal.addTag(`go_golem_start:${now}`);
            }

            const startTag = animal.getTags().find(t => t.startsWith("go_golem_start:"));
            if (startTag) {
                const startTime = parseInt(startTag.split(":")[1]);
                if (now - startTime >= GIVE_UP_TIME) {
                    animal.removeTag("going_to_golem");
                    animal.removeTag(startTag);

                    // console.warn(`failed to breedable, cooldown: ${GIVE_UP_TIME_COOLDOWN}`)

                    animal.addTag("failed_attempt")
                    animal.addTag(`failed_cooldown:${Math.floor(Date.now() / 1000)}`);
                    return;
                }
            }

            const animalPos = animal.location;
            const dx = golemPos.x - animalPos.x;
            const dz = golemPos.z - animalPos.z;
            const distance = this.distance(golemPos, animalPos);

            if (distance === 0) return;

            const direction = {
                x: dx / distance,
                y: 0,
                z: dz / distance
            };

            const rotation = this.getRotationTo(animal.location, golem.location);
            animal.setRotation({ x: 0, y: rotation });

            const checkX = Math.floor(animalPos.x + direction.x);
            const checkY = Math.floor(animalPos.y);
            const checkZ = Math.floor(animalPos.z + direction.z);

            const blockInFront = animal.dimension.getBlock({ x: checkX, y: checkY, z: checkZ });
            const shouldJump = blockInFront && !blockInFront.isAir;

            animal.applyImpulse({
                x: direction.x * PULL_STRENGTH,
                y: shouldJump ? 0.12 : 0,
                z: direction.z * PULL_STRENGTH
            });
        });
    }
    breedable(animal) {
        try {
            const timestamp = Math.floor(Date.now() / 1000);

            animal.addTag("breedable")
            animal.addTag(`lb_ga_date:${timestamp}`)
            animal.dimension.spawnParticle("minecraft:heart_particle", { x: animal.location.x, y: animal.location.y + 1.5, z: animal.location.z })
        } catch (e) {

        }
    }
    /**
     * 
     * @param {Entity} animal 
     */
    newBabyAnimal(animal) {
        try {
            animal.dimension.spawnParticle("minecraft:heart_particle", { x: animal.location.x, y: animal.location.y + 1.5, z: animal.location.z })
            system.runTimeout(() => {

                //*If you spawn it, it won't disappear, but if you add the event directly to the command, it will turn into a default cow (black and white).
                animal.runCommand(`/summon ${animal.typeId} ~ ~ ~ ~ ~ minecraft:entity_born`)

                const newAnimal = animal.dimension.getEntities({
                    type: animal.typeId,
                    location: animal.location,
                    maxDistance: 1.5,
                    excludeTags: ["breedable", "baby_protected"]

                }).at(0);

                if (newAnimal.typeId == "minecraft:cow") {
                    newAnimal.setProperty('minecraft:climate_variant', animal.getProperty('minecraft:climate_variant'))
                }
            }, 30)
        } catch (e) {
        }
    }
    distance(pos1, pos2) {
        const dx = pos1.x - pos2.x;
        const dy = pos1.y - pos2.y;
        const dz = pos1.z - pos2.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }
    getRotationTo(from, to) {
        const direction = calculateDirection(from, to)[0]

        const angle_rad = Math.atan2(direction.z, direction.x)

        let angle_deg = (angle_rad * (180 / Math.PI)) - 90
        if (angle_deg > 180) {
            angle_deg -= 360
        } else if (angle_deg < -180) {
            angle_deg += 360
        }

        return angle_deg
    }
}

const HGMain = new HayGolemMain()

export function calculateDirection(fromPoint, toPoint) {
    // Calcular as diferenças entre as coordenadas dos pontos
    const deltaX = toPoint.x - fromPoint.x;
    const deltaY = toPoint.y - fromPoint.y;
    const deltaZ = toPoint.z - fromPoint.z;

    // Calcular o comprimento do vetor
    const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ);

    let direction
    // Verificar se o comprimento é zero (para evitar divisão por zero)
    if (length === 0) {
        direction = { x: 0, y: 0, z: 0 }; // Ponto de origem e destino são iguais
    } else {
        direction = {
            z: deltaZ / length,
            y: deltaY / length,
            x: deltaX / length
        }
    }

    // Escalar o vetor para o intervalo de -1 a 1
    const maxComponent = Math.max(Math.abs(direction.x), Math.abs(direction.y), Math.abs(direction.z));
    if (maxComponent > 1) {
        direction.x /= maxComponent;
        direction.y /= maxComponent;
        direction.z /= maxComponent;
    }

    return [direction, length];
}

function hayForm(golem, player) {
    const form = new ActionFormData()

    const wheat = golem.getProperty("lb_ga:hay_golem_wheat")
    const breedableAnimals = golem.getDynamicProperty("lb_ga:hay_golem_animals_breed")
    form.title({ translate: "form.hay.title" })

    const bodyText = {
        rawtext: [
            { text: "\n" },
            {
                translate: "form.hay.body.wheatremain",
                with: [`${wheat}`, `${MAX_WHEAT_CAPACITY}`]
            },
            { text: "\n\n" },
            {
                translate: "form.hay.body.breedable",
                with: [`${breedableAnimals}`]
            },
            { text: "\n\n" }
        ]
    }
    form.body(bodyText)
    form.button({ translate: "text.button.ok" })

    form.show(player)
}

world.beforeEvents.playerInteractWithEntity.subscribe(event => {
    if (event.target.typeId !== "lb_ga:hay_golem") return
    if (event.player.getComponent("equippable")?.getEquipment("Mainhand")?.typeId === "minecraft:lead") return

    const item = event.player.getComponent("inventory").container.getItem(event.player.selectedSlotIndex);
    if (!item || item.typeId != "minecraft:wheat") {
        system.run(() => {
            hayForm(event.target, event.player);
        });
        return;
    }

    if (item.typeId === "minecraft:wheat") {
        const currentWheat = event.target.getProperty("lb_ga:hay_golem_wheat") || 0;

        if (currentWheat < MAX_WHEAT_CAPACITY) {
            system.run(() => {
                event.target.setProperty("lb_ga:hay_golem_wheat", currentWheat + 1);
                const pos = event.target.location;
                event.target.dimension.spawnParticle("minecraft:crop_growth_emitter", {
                    x: pos.x,
                    y: pos.y + 2,
                    z: pos.z
                });

                event.player.runCommand('/clear @s wheat 0 1')
            })
            return;
        }
    }
});

const dimensions = ["overworld", "nether", "the_end"]

system.runInterval(() => {
    dimensions.forEach(dimension => {
        dimension = world.getDimension(dimension)
        const entities = dimension.getEntities({ type: "lb_ga:hay_golem" })
        entities.forEach(entity => {
            HGMain.main(entity)
        })
    })
})

system.runInterval(() => {//*REMOVE TAG FOR ANIMALS
    dimensions.forEach(dimension => {
        dimension = world.getDimension(dimension)

        for (const entity of dimension.getEntities({ tags: ["breedable"] })) {
            const timeTag = entity.getTags().find(tag => tag.startsWith("lb_ga_date:"));
            if (!timeTag) continue;

            const savedTime = parseInt(timeTag.split(":")[1]);
            const currentTime = Math.floor(Date.now() / 1000);

            if (currentTime - savedTime >= TIME_TO_NEXT_BREEDABLE) {
                entity && entity.removeTag("breedable");
                entity && entity.removeTag(timeTag);
            }
        }

        for (const entity of dimension.getEntities({ tags: ["failed_attempt"] })) {
            const failed_cooldown = entity.getTags().find(tag => tag.startsWith("failed_cooldown:"));
            if (!failed_cooldown) continue;

            const savedTime = parseInt(failed_cooldown.split(":")[1]);
            const currentTime = Math.floor(Date.now() / 1000);

            if (currentTime - savedTime >= GIVE_UP_TIME_COOLDOWN) {
                entity && entity.removeTag("failed_attempt");
                entity && entity.removeTag(failed_cooldown);

                // console.warn(`Animals can breedable`)
            }
        }
    })
}, 20)

world.afterEvents.dataDrivenEntityTrigger.subscribe((data) => {
    if (data.eventId == "minecraft:ageable_grow_up") {
        data.entity.removeTag("baby_protected")
    }
    data.getModifiers().forEach((modifier) => {
        if (modifier.addedComponentGroups.includes("minecraft:cow_baby") ||
            modifier.addedComponentGroups.includes("minecraft:sheep_baby") ||
            modifier.addedComponentGroups.includes("minecraft:goat_baby")) {
            data.entity && data.entity.addTag("baby_protected")
        }
    })
})

world.afterEvents.entityDie.subscribe(deathEvent => {
    const entity = deathEvent.deadEntity
    if (entity.typeId !== "lb_ga:hay_golem") return
    let remainingWheat = entity.getProperty('lb_ga:hay_golem_wheat')
    while (remainingWheat > 0) {
        let thisStack = 64
        if (remainingWheat < 64) {
            thisStack = remainingWheat
        }
        entity.dimension.spawnItem(new ItemStack("minecraft:wheat", thisStack), entity.location)
        remainingWheat -= thisStack
    }
})