import { world, system } from "@minecraft/server";
import { fo } from './utilities/fo_constants.js';



export function initTunnelLoop() {

    const FOREBODINGCHECK2 = fo.scoreChapter4; 

    const TUNNEL_DATA_KEY = "tunnelData";
    const playerTrack = new Map();

    const AIR_TYPES = new Set(["minecraft:air", "minecraft:torch"]);
    const SOLID_EXCEPTIONS = new Set(["minecraft:torch"]); 

    let eventFlag = false;

    
    function isSolidBlock(block) {
        return block && !AIR_TYPES.has(block.typeId) && !SOLID_EXCEPTIONS.has(block.typeId);
    }

    
    function isAirBlock(block) {
        return block && AIR_TYPES.has(block.typeId);
    }

    system.runInterval(() => {
        const forebodingScore = fo.forebodingScore;
        const dim = fo.overworld;

        const savedTunnelData = world.getDynamicProperty(TUNNEL_DATA_KEY);

        if (savedTunnelData) {
            let parsedTunnelData = JSON.parse(savedTunnelData);

            if (parsedTunnelData?.tunnelLoop) {
                cleanupTunnelLoop(dim, parsedTunnelData, null);
                return;
            }

            if (parsedTunnelData?.cooldown > 0) {
                parsedTunnelData.cooldown--
                world.setDynamicProperty(TUNNEL_DATA_KEY, JSON.stringify(parsedTunnelData));

                
                return;
            }
        };

        if (forebodingScore < FOREBODINGCHECK2) return;

        for (const player of dim.getPlayers()) {
            if (!player?.location) continue;
            const { x, y, z } = player.location;
            if (y >= 20) continue; 

            const id = player.id;
            const last = playerTrack.get(id);

            
            if (last) {
                const dx = x - last.x;
                const dy = y - last.y;
                const dz = z - last.z;
                const dist = Math.sqrt(dx * dx + dz * dz);

                
                if (dist > 1.5 && Math.abs(dy) < 0.5) {
                    const dir = getDirection(dx, dz);
                    const prevDir = last.direction;

                    
                    if (!prevDir || dir === prevDir) {
                        playerTrack.set(id, { x, y, z, direction: dir, stable: (last.stable || 0) + 1 });

                        
                        if ((last.stable || 0) === 5) analyzeTunnelFromPosition(player, dir);
                    } else {
                        playerTrack.set(id, { x, y, z, direction: dir, stable: 0 });
                    }
                } else {
                    playerTrack.set(id, { x, y, z, direction: null, stable: 0 });
                }
            } else {
                playerTrack.set(id, { x, y, z, direction: null, stable: 0 });
            }
        }
    }, 10); 

    function getDirection(dx, dz) {
        if (Math.abs(dx) > Math.abs(dz)) return dx > 0 ? "x+" : "x-";
        return dz > 0 ? "z+" : "z-";
    }

    function analyzeTunnelFromPosition(player, dir) {
        

        const dim = player.dimension;
        const pos = player.location;
        const axis = dir[0]; 
        const sign = dir[1] === "+" ? 1 : -1;

        const nearbyPlayers = dim.getPlayers({
            location: pos,
            maxDistance: 128,
        });
        if (nearbyPlayers.length > 1 || eventFlag) return;

        const tunnel = verifyTunnelStructure(dim, pos, axis);
        if (!tunnel) return; 

        const { height, width, ceilingHit, leftWallHit, rightWallHit } = tunnel;
        if (height > 3 || width > 3) return; 
        const corners = traceTunnelLength(dim, pos, axis, sign, height, width, ceilingHit, leftWallHit, rightWallHit);

        const tunnelData = {
            tunnelLoop: false,
            direction: dir,
            width,
            height,
            start: corners.start,
            end: corners.end,
            length: corners.length,
            cooldown: 115200 
        };

        if (tunnelData.length < 25) return; 

        system.runTimeout(() => {
            eventFlag = true;
            tunnelData.tunnelLoop = true;
            world.setDynamicProperty(TUNNEL_DATA_KEY, JSON.stringify(tunnelData));

            
            

            tunnelLoop(player, tunnelData);
        }, 1)
    }

    function verifyTunnelStructure(dim, pos, axis) {
        const base = {
            x: Math.floor(pos.x),
            y: Math.floor(pos.y),
            z: Math.floor(pos.z)
        };

        
        const floor = dim.getBlock({ ...base, y: base.y - 1 });
        const head = dim.getBlock({ ...base, y: base.y + 1 });

        if (!floor || !isSolidBlock(floor)) return null;
        if (!head || !isAirBlock(head)) return null;

        const ceilingHit = dim.getBlockFromRay(
            { x: pos.x, y: pos.y + 1, z: pos.z }, 
            { x: 0, y: 1, z: 0 }, 
            {
                includeLiquidBlocks: false,
                includePassableBlocks: false,
                maxDistance: 4
            }
        )?.block;

        if (!ceilingHit || !isSolidBlock(ceilingHit)) return null;

        const height = Math.floor(ceilingHit.location.y - base.y);

        
        const perp = axis === "x" ? "z" : "x";

        
        const leftDir = {
            x: perp === "x" ? -1 : 0,
            y: 0,
            z: perp === "z" ? -1 : 0
        };

        const leftWallHit = dim.getBlockFromRay(
            pos,
            leftDir,
            {
                includeLiquidBlocks: false,
                includePassableBlocks: false,
                maxDistance: 4
            }
        )?.block;

        
        const rightDir = {
            x: perp === "x" ? 1 : 0,
            y: 0,
            z: perp === "z" ? 1 : 0
        };

        const rightWallHit = dim.getBlockFromRay(
            pos,
            rightDir,
            {
                includeLiquidBlocks: false,
                includePassableBlocks: false,
                maxDistance: 4
            }
        )?.block;

        if (!leftWallHit || !isSolidBlock(leftWallHit) ||
            !rightWallHit || !isSolidBlock(rightWallHit)) {
            return null;
        }

        
        const leftDistance = perp === "x" ? Math.floor(leftWallHit.location.x - pos.x) : Math.floor(leftWallHit.location.z - pos.z);
        const rightDistance = perp === "x" ? Math.floor(rightWallHit.location.x - pos.x) : Math.floor(rightWallHit.location.z - pos.z);

        const width = Math.abs(leftDistance) + Math.abs(rightDistance) - 1; 

        return { height, width, ceilingHit, leftWallHit, rightWallHit };
    }


    function traceTunnelLength(dim, pos, axis, sign, height, width, ceilingHit, leftWallHit, rightWallHit) {

        const startCenter = {
            x: Math.floor(pos.x),
            y: Math.floor(pos.y),
            z: Math.floor(pos.z)
        };
        let endCenter = { ...startCenter };

        
        for (let i = 2; i <= 32; i += 3) {
            const next = {
                x: axis === "x" ? startCenter.x + i * sign : startCenter.x,
                y: startCenter.y,
                z: axis === "z" ? startCenter.z + i * sign : startCenter.z
            };

            const center = dim.getBlock(next);
            if (!center || !isAirBlock(center)) break;

            const check = verifyTunnelStructure(dim, next, axis);
            if (!check || check.height !== height || check.width !== width) break;

            endCenter = next;
        }
        const xLength = Math.abs(endCenter.x - startCenter.x) - 1;
        const zLength = Math.abs(endCenter.z - startCenter.z) - 1;

        const length = Math.max(xLength, zLength) + 1;

        let startCorner, endCorner, startCornerOrdered, endCornerOrdered;

        if (axis === "x") {
            startCorner = {
                x: leftWallHit.location.x + xLength * sign,
                y: Math.floor(pos.y) - 1, 
                z: leftWallHit.location.z
            };
            endCorner = {
                x: rightWallHit.location.x,
                y: ceilingHit.location.y, 
                z: rightWallHit.location.z
            };
        } else { 
            startCorner = {
                x: leftWallHit.location.x,
                y: Math.floor(pos.y) - 1, 
                z: leftWallHit.location.z
            };
            endCorner = {
                x: rightWallHit.location.x,
                y: ceilingHit.location.y, 
                z: rightWallHit.location.z + zLength * sign
            };
        }

        startCornerOrdered = {
            x: Math.floor(Math.min(startCorner.x, endCorner.x)),
            y: Math.floor(Math.min(startCorner.y, endCorner.y)),
            z: Math.floor(Math.min(startCorner.z, endCorner.z))
        };
        endCornerOrdered = {
            x: Math.floor(Math.max(startCorner.x, endCorner.x)),
            y: Math.floor(Math.max(startCorner.y, endCorner.y)),
            z: Math.floor(Math.max(startCorner.z, endCorner.z))
        };

        startCornerOrdered = {
            x: startCornerOrdered.x < 0 ? startCornerOrdered.x - 0.999 : startCornerOrdered.x,
            y: startCornerOrdered.y < 0 ? startCornerOrdered.y - 0.999 : startCornerOrdered.y,
            z: startCornerOrdered.z < 0 ? startCornerOrdered.z - 0.999 : startCornerOrdered.z,
        };
        endCornerOrdered = {
            x: endCornerOrdered.x < 0 ? endCornerOrdered.x : endCornerOrdered.x + 0.999,
            y: endCornerOrdered.y < 0 ? endCornerOrdered.y : endCornerOrdered.y + 0.999,
            z: endCornerOrdered.z < 0 ? endCornerOrdered.z : endCornerOrdered.z + 0.999,
        };

        return {
            start: startCornerOrdered,
            end: endCornerOrdered,
            length
        };
    };


    
    

    
    function saveStructure(dimension, startCoords, endCoords, structureName) {
        const structureManager = world.structureManager;
        const saveOptions = {
            saveMode: "World", 
            includeEntities: false,
            includeBlocks: true,
        };
        

        structureManager.createFromWorld(
            structureName,
            dimension,
            { x: startCoords.x, y: startCoords.y, z: startCoords.z },
            { x: endCoords.x, y: endCoords.y, z: endCoords.z },
            saveOptions
        );
        return true;
    }

    function placeStructure(dimension, startCoords, structureName) {
        const structureManager = world.structureManager;
        const placeOptions = {
            animationMode: "None",
            
        };

        structureManager.place(
            structureName,
            dimension,
            { x: startCoords.x, y: startCoords.y, z: startCoords.z }, 
            placeOptions
        );
        return true;
    }

    function tunnelLoop(player, tunnelData) {
        const dim = player.dimension;
        const structureBaseName = `fo:${player.name}${Math.floor(Math.random() * 10000)}`;
        const originalStructureName = `${structureBaseName}Original`;
        const startCopyStructureName = `${structureBaseName}StartCopyArea`;
        const endCopyStructureName = `${structureBaseName}EndCopyArea`;


        
        const { startCopy, endCopy } = calculateCopyTunnelPositions(tunnelData);

        
        tunnelData.structures = {
            originalStructure: originalStructureName,
            startCopyArea: startCopyStructureName,
            endCopyArea: endCopyStructureName,
            startTunnelPos: startCopy.startOrdered,
            startTunnelEnd: startCopy.endOrdered,
            endTunnelPos: endCopy.startOrdered,
            endTunnelEnd: endCopy.endOrdered,
            dimension: dim
        };

        if (!saveStructure(dim, tunnelData.start, tunnelData.end, originalStructureName)) {
            console.error("Failed to save original tunnel structure");
            return;
        }

        if (!saveStructure(dim, startCopy.startOrdered, startCopy.endOrdered, startCopyStructureName) ||
            !saveStructure(dim, endCopy.startOrdered, endCopy.endOrdered, endCopyStructureName)) {
            console.error("Failed to save copy area structures");
            return;
        }

        world.setDynamicProperty(TUNNEL_DATA_KEY, JSON.stringify(tunnelData));



        

        system.runTimeout(() => {
            player.runCommand(`fog @s push foreboding:fog08ub Fog`);
            player.camera.fade({
                fadeTime: {
                    fadeInTime: 0.0,
                    holdTime: 0.2,
                    fadeOutTime: 0.3,
                },
                fadeColor: {
                    red: 0,
                    green: 0,
                    blue: 0,
                },
            });

            placeStructure(dim, startCopy.startOrdered, originalStructureName); 
            placeStructure(dim, endCopy.startOrdered, originalStructureName);   
            
            startTunnelMonitoring(player, tunnelData);
        }, 60)
    }

    function calculateCopyTunnelPositions(tunnelData) {
        const axis = tunnelData.direction[0];
        const sign = tunnelData.direction[1] === "+" ? 1 : -1;

        const length = tunnelData.length;

        let startCopy, endCopy;

        if (axis === 'x') {
            
            startCopy = {
                start: {
                    x: tunnelData.start.x < 0 ? tunnelData.start.x - length + 2 : tunnelData.start.x - length,
                    y: tunnelData.start.y,
                    z: tunnelData.start.z
                },
                end: {
                    x: tunnelData.start.x < 0 ? tunnelData.start.x - 0.001 : tunnelData.start.x - 0.001,
                    y: tunnelData.end.y,
                    z: tunnelData.end.z
                }
            };

            
            endCopy = {
                start: {
                    x: tunnelData.end.x < 0 ? tunnelData.end.x + 0.001 : tunnelData.end.x + 0.001,
                    y: tunnelData.start.y,
                    z: tunnelData.start.z
                },
                end: {
                    x: tunnelData.end.x < 0 ? tunnelData.end.x + length - 2 : tunnelData.end.x + length,
                    y: tunnelData.end.y,
                    z: tunnelData.end.z
                }
            };
        } else { 
            
            startCopy = {
                start: {
                    x: tunnelData.start.x,
                    y: tunnelData.start.y,
                    z: tunnelData.start.z < 0 ? tunnelData.start.z - length + 2 : tunnelData.start.z - length,
                },
                end: {
                    x: tunnelData.end.x,
                    y: tunnelData.end.y,
                    z: tunnelData.start.z < 0 ? tunnelData.start.z - 0.001 : tunnelData.start.z - 0.001,
                }
            };

            
            endCopy = {
                start: {
                    x: tunnelData.start.x,
                    y: tunnelData.start.y,
                    z: tunnelData.end.z < 0 ? tunnelData.end.z + 0.001 : tunnelData.end.z + 0.001,
                },
                end: {
                    x: tunnelData.end.x,
                    y: tunnelData.end.y,
                    z: tunnelData.end.z < 0 ? tunnelData.end.z + length - 2 : tunnelData.end.z + length,
                }
            };
        }

        startCopy.startOrdered = {
            x: Math.min(startCopy.start.x, startCopy.end.x),
            y: Math.min(startCopy.start.y, startCopy.end.y),
            z: Math.min(startCopy.start.z, startCopy.end.z)
        };
        startCopy.endOrdered = {
            x: Math.max(startCopy.start.x, startCopy.end.x),
            y: Math.max(startCopy.start.y, startCopy.end.y),
            z: Math.max(startCopy.start.z, startCopy.end.z)
        };

        endCopy.startOrdered = {
            x: Math.min(endCopy.start.x, endCopy.end.x),
            y: Math.min(endCopy.start.y, endCopy.end.y),
            z: Math.min(endCopy.start.z, endCopy.end.z)
        };
        endCopy.endOrdered = {
            x: Math.max(endCopy.start.x, endCopy.end.x),
            y: Math.max(endCopy.start.y, endCopy.end.y),
            z: Math.max(endCopy.start.z, endCopy.end.z)
        };


        return { startCopy, endCopy };
    }

    function startTunnelMonitoring(player, tunnelData) {
        const monitorInterval = system.runInterval(() => {
            
            if (player.dimension.id !== "minecraft:overworld") {
                cleanupTunnelLoop(player.dimension, tunnelData, player);
                system.clearRun(monitorInterval)
                return;
            }

            
            const nearbyPlayers = player.dimension.getPlayers({
                location: player.location,
                maxDistance: 128
            });

            if (nearbyPlayers.length > 1 || nearbyPlayers.length === 0) {
                
                cleanupTunnelLoop(player.dimension, tunnelData, player);
                system.clearRun(monitorInterval)
                return;
            }

            
            const inOriginalTunnel = isInTunnelArea(player.location, tunnelData.start, tunnelData.end, tunnelData.direction[0]);
            const inStartTunnel = isInTunnelArea(player.location, tunnelData.structures.startTunnelPos, tunnelData.structures.startTunnelEnd, tunnelData.direction[0]);
            const inEndTunnel = isInTunnelArea(player.location, tunnelData.structures.endTunnelPos, tunnelData.structures.endTunnelEnd, tunnelData.direction[0]);

            
            
            

            if (inStartTunnel) {
                teleportToCounterpart(player, tunnelData, "start");
            } else if (inEndTunnel) {
                teleportToCounterpart(player, tunnelData, "end");
            } else if (!inOriginalTunnel) {
                
                
                cleanupTunnelLoop(player.dimension, tunnelData, player);
                system.clearRun(monitorInterval)
            }

        }, 5); 
    }

    function isInTunnelArea(location, startCorner, endCorner, axis) {
        
        const min = { ...startCorner };
        const max = { ...endCorner };

        
        if (axis === "x") {
            min.z < 0 ? min.z += 1.999 : min.z += 0.8;
            max.z < 0 ? max.z : max.z -= 1;
        } else if (axis === "z") {
            min.x < 0 ? min.x += 1.999 : min.x += 0.8;
            max.x < 0 ? max.x : max.x -= 1;
        }

        

        const inside =
            location.x >= min.x && location.x <= max.x &&
            location.y >= min.y && location.y <= max.y &&
            location.z >= min.z && location.z <= max.z;

        return inside;
    }

    function teleportToCounterpart(player, tunnelData, fromTunnel) {
        player.runCommand(`camerashake add @s 1 0.2 rotational`);

        const playerLoc = player.location;
        const axis = tunnelData.direction[0];  
        const dirSign = tunnelData.direction[1] === "+" ? 1 : -1;
        const length = tunnelData.length;

        
        const offset = (fromTunnel === "start" ? 1 : -1) * length;

        const targetPos =
            axis === "x"
                ? { x: playerLoc.x + offset, y: playerLoc.y, z: playerLoc.z }
                : { x: playerLoc.x, y: playerLoc.y, z: playerLoc.z + offset };

        
        const safePos = {
            x: Math.floor(targetPos.x),
            y: targetPos.y,
            z: Math.floor(targetPos.z)
        };

        player.teleport(targetPos, { dimension: player.dimension });

        
    }

    function cleanupTunnelLoop(dimension, tunnelData, player) {
        if (player) player.runCommand(`fog @s remove Fog`);
        dimension.runCommand(`tickingarea add ${tunnelData.structures.startTunnelPos.x - 3} -60 ${tunnelData.structures.startTunnelPos.z - 3} ${tunnelData.structures.endTunnelEnd.x + 3} 80 ${tunnelData.structures.endTunnelEnd.z + 3} foTunnelLoop`)

        system.runTimeout(() => {
            dimension.runCommand(`tickingarea remove foTunnelLoop`)

            
            placeStructure(dimension, tunnelData.structures.startTunnelPos, tunnelData.structures.startCopyArea);
            placeStructure(dimension, tunnelData.structures.endTunnelPos, tunnelData.structures.endCopyArea);

            
            system.runTimeout(() => {
                try {
                    world.structureManager.delete(tunnelData.structures.originalStructure);
                    world.structureManager.delete(tunnelData.structures.startCopyArea);
                    world.structureManager.delete(tunnelData.structures.endCopyArea);
                } catch (e) {
                    console.error("Error deleting structures:", e);
                }
            }, 200); 

            eventFlag = false;
            tunnelData.tunnelLoop = false;
            world.setDynamicProperty(TUNNEL_DATA_KEY, JSON.stringify(tunnelData));

            
        }, 30)
    }

}