// Enhanced Golf Ball Precise Location Tracking System
// Stores exact ball positions and handles teleport-back functionality

import { world, system } from "@minecraft/server";
import { BLOCK_IDS, DECOR_BLOCKS } from './blocks.js';
import { debug } from './utils.js';

// Partial-safe blocks that balls can get stuck against (non-full blocks)
const PARTIAL_SAFE_BLOCKS = [
    // Glass panes and iron bars
    "minecraft:glass_pane",
    "minecraft:white_stained_glass_pane",
    "minecraft:orange_stained_glass_pane",
    "minecraft:magenta_stained_glass_pane",
    "minecraft:light_blue_stained_glass_pane",
    "minecraft:yellow_stained_glass_pane",
    "minecraft:lime_stained_glass_pane",
    "minecraft:pink_stained_glass_pane",
    "minecraft:gray_stained_glass_pane",
    "minecraft:light_gray_stained_glass_pane",
    "minecraft:cyan_stained_glass_pane",
    "minecraft:purple_stained_glass_pane",
    "minecraft:blue_stained_glass_pane",
    "minecraft:brown_stained_glass_pane",
    "minecraft:green_stained_glass_pane",
    "minecraft:red_stained_glass_pane",
    "minecraft:black_stained_glass_pane",
    "minecraft:iron_bars",
    
    // Fences and gates
    "fence", // Partial match for all fence types
    "gate", // Partial match for all gate types
    
    // Plants and vegetation
    "minecraft:kelp",
    "minecraft:kelp_plant",
    "minecraft:seagrass",
    "minecraft:tall_seagrass",
    "minecraft:grass",
    "minecraft:tall_grass",
    "minecraft:fern",
    "minecraft:large_fern",
    "minecraft:dead_bush",
    "minecraft:dandelion",
    "minecraft:poppy",
    "minecraft:blue_orchid",
    "minecraft:allium",
    "minecraft:azure_bluet",
    "minecraft:red_tulip",
    "minecraft:orange_tulip",
    "minecraft:white_tulip",
    "minecraft:pink_tulip",
    "minecraft:oxeye_daisy",
    "minecraft:cornflower",
    "minecraft:lily_of_the_valley",
    "minecraft:sunflower",
    "minecraft:lilac",
    "minecraft:rose_bush",
    "minecraft:peony",
    "minecraft:sweet_berry_bush",
    
    // Crops and farmland plants
    "minecraft:wheat",
    "minecraft:carrots",
    "minecraft:potatoes",
    "minecraft:beetroots",
    "minecraft:sugar_cane",
    "minecraft:bamboo",
    "minecraft:bamboo_sapling",
    
    // Mushrooms and fungi
    "minecraft:red_mushroom",
    "minecraft:brown_mushroom",
    "minecraft:crimson_fungus",
    "minecraft:warped_fungus",
    "minecraft:crimson_roots",
    "minecraft:warped_roots",
    "minecraft:nether_sprouts",
    
    // Torches and light sources
    "torch", // Partial match for all torch types
    "minecraft:lantern",
    "minecraft:soul_lantern",
    "minecraft:sea_pickle",
    
    // Redstone components
    "minecraft:redstone_wire",
    "minecraft:redstone_torch",
    "minecraft:lever",
    "button", // Partial match for all button types
    "pressure_plate", // Partial match for all pressure plates
    
    // Signs and item frames
    "sign", // Partial match for all sign types
    "minecraft:item_frame",
    "minecraft:glow_item_frame",
    
    // Chains and ropes
    "minecraft:chain",
    "minecraft:lightning_rod",
    
    // Carpet and thin blocks
    "carpet", // Partial match for all carpet types
    "minecraft:snow", // Snow layer (not snow block)
    
    // Slabs (when not full)
    "slab" // Partial match for all slab types
];

// Store precise landing positions for each ball
const ballLandingHistory = new Map(); // ballId -> array of precise positions
const ballCurrentPreciseLocation = new Map(); // ballId -> current precise location
const ballValidLocation = new Map(); // ballId -> single valid location (constantly updated)
const ballSpawnTime = new Map(); // ballId -> spawn timestamp to prevent immediate validation
const ballStationaryTime = new Map(); // ballId -> timestamp when ball became stationary

// Constants for tracking
const MAX_LANDING_HISTORY = 10; // Keep last 10 landing positions
const PRECISION_THRESHOLD = 0.01; // Minimum movement to consider as new position
const OUT_OF_BOUNDS_CHECK_RADIUS = 50; // Blocks to check around for valid course blocks
const STATIONARY_VELOCITY_THRESHOLD = 0.05; // Ball considered stationary below this velocity
const VALIDATOR_CHECK_INTERVAL = 5; // Check every 5 ticks (0.25 seconds) for continuous monitoring
const SPAWN_GRACE_PERIOD = 60; // Ticks to wait after spawn before validating (3 seconds)
const MID_AIR_CHECK_DEPTH = 3; // Blocks to check below ball to ensure it's on solid ground
const STATIONARY_CHECK_TICKS = 10; // Ticks ball must be stationary before checking partial blocks

/**
 * Store precise ball location when it lands
 * @param {Entity} golfball - The golf ball entity
 * @param {Object} preciseLocation - Exact location with decimals {x, y, z}
 * @param {string} surfaceType - Type of surface landed on
 * @param {boolean} isGolfValid - Whether this is valid according to golf rules (optional, defaults to tier-based validation)
 */
export function storePreciseLanding(golfball, preciseLocation, surfaceType, isGolfValid = null) {
    if (!golfball || !preciseLocation) return;
    
    const ballId = golfball.id;
    const timestamp = Date.now();
    const validityTier = getLocationValidityTier(surfaceType);
    
    // Determine if location is valid according to golf rules
    // If isGolfValid is explicitly provided, use that; otherwise fall back to tier-based validation
    const isValidByGolfRules = isGolfValid !== null ? isGolfValid : (validityTier === 'perfect' || validityTier === 'good');
    
    // Create landing record with full precision
    const landingRecord = {
        x: preciseLocation.x,
        y: preciseLocation.y,
        z: preciseLocation.z,
        timestamp: timestamp,
        surfaceType: surfaceType,
        validityTier: validityTier,
        isValid: isValidByGolfRules
    };
    
    // Update current precise location
    ballCurrentPreciseLocation.set(ballId, landingRecord);
    
    // Add to landing history
    let history = ballLandingHistory.get(ballId) || [];
    history.push(landingRecord);
    
    // Keep only recent history
    if (history.length > MAX_LANDING_HISTORY) {
        history = history.slice(-MAX_LANDING_HISTORY);
    }
    ballLandingHistory.set(ballId, history);
    
    // SIMPLIFIED: Only update single valid location if this is truly valid according to golf rules
    if (isValidByGolfRules) {
        ballValidLocation.set(ballId, {
            x: preciseLocation.x,
            y: preciseLocation.y,
            z: preciseLocation.z,
            timestamp: timestamp,
            validityTier: validityTier
        });
        debug('info', `Stored VALID landing for ball ${ballId}: (${preciseLocation.x.toFixed(3)}, ${preciseLocation.y.toFixed(3)}, ${preciseLocation.z.toFixed(3)}) on ${surfaceType} [${validityTier}]`);
    } else {
        debug('verbose', `Stored landing for ball ${ballId}: (${preciseLocation.x.toFixed(3)}, ${preciseLocation.y.toFixed(3)}, ${preciseLocation.z.toFixed(3)}) on ${surfaceType} [${validityTier}] - NOT golf-valid`);
    }
}

/**
 * Store invalid ball location for tracking without overwriting valid locations
 * @param {Entity} golfball - The golf ball entity
 * @param {Object} preciseLocation - Exact location with decimals {x, y, z}
 * @param {string} surfaceType - Type of surface landed on
 */
export function storeInvalidLanding(golfball, preciseLocation, surfaceType) {
    if (!golfball || !preciseLocation) return;
    
    const ballId = golfball.id;
    const timestamp = Date.now();
    const validityTier = getLocationValidityTier(surfaceType);
    
    // Create landing record with full precision but mark as invalid
    const landingRecord = {
        x: preciseLocation.x,
        y: preciseLocation.y,
        z: preciseLocation.z,
        timestamp: timestamp,
        surfaceType: surfaceType,
        validityTier: validityTier,
        isValid: false // Always false for invalid landings
    };
    
    // Update current precise location (for tracking purposes)
    ballCurrentPreciseLocation.set(ballId, landingRecord);
    
    // Add to landing history (for debugging/analysis)
    let history = ballLandingHistory.get(ballId) || [];
    history.push(landingRecord);
    
    // Keep only recent history
    if (history.length > MAX_LANDING_HISTORY) {
        history = history.slice(-MAX_LANDING_HISTORY);
    }
    ballLandingHistory.set(ballId, history);
    
    // NEVER update ballValidLocation for invalid landings
    
    debug('warn', `Stored INVALID landing for ball ${ballId}: (${preciseLocation.x.toFixed(3)}, ${preciseLocation.y.toFixed(3)}, ${preciseLocation.z.toFixed(3)}) on ${surfaceType} [${validityTier}]`);
}

/**
 * Get precise current location of a golf ball (not rounded)
 * @param {Entity} golfball - The golf ball entity
 * @returns {Object|null} Precise location {x, y, z} or null
 */
export function getPreciseBallLocation(golfball) {
    if (!golfball || !golfball.location) return null;
    
    return {
        x: golfball.location.x,
        y: golfball.location.y,
        z: golfball.location.z
    };
}

/**
 * Check if ball has moved significantly from last recorded position
 * @param {Entity} golfball - The golf ball entity
 * @returns {boolean} True if ball has moved significantly
 */
export function hasMovedSignificantly(golfball) {
    if (!golfball) return false;
    
    const ballId = golfball.id;
    const currentLocation = ballCurrentPreciseLocation.get(ballId);
    
    if (!currentLocation) return true; // First time tracking
    
    const newLocation = getPreciseBallLocation(golfball);
    if (!newLocation) return false;
    
    const distance = Math.sqrt(
        Math.pow(newLocation.x - currentLocation.x, 2) +
        Math.pow(newLocation.y - currentLocation.y, 2) +
        Math.pow(newLocation.z - currentLocation.z, 2)
    );
    
    return distance > PRECISION_THRESHOLD;
}

/**
 * Determine if a surface type represents a valid landing location
 * Uses a tiered approach: Perfect > Good > Acceptable > Emergency
 * @param {string} surfaceType - The surface type identifier
 * @returns {string} Validity tier: 'perfect', 'good', 'acceptable', 'emergency', or 'invalid'
 */
function getLocationValidityTier(surfaceType) {
    // Perfect golf course surfaces (ideal for play)
    const perfectSurfaces = [
        BLOCK_IDS.FAIRWAY_LIGHT,
        BLOCK_IDS.FAIRWAY_LIGHT_SLAB,
        BLOCK_IDS.FAIRWAY_MID,
        BLOCK_IDS.FAIRWAY_MID_SLAB,
        BLOCK_IDS.GREEN,
        BLOCK_IDS.GREEN_SLAB,
        "spawn_location" // Always consider spawn location as perfect
    ];
    
    // Good surfaces (playable but not ideal)
    const goodSurfaces = [
        BLOCK_IDS.ROUGH,
        BLOCK_IDS.ROUGH_OUTLINED,
        BLOCK_IDS.ROUGH_SLAB,
        BLOCK_IDS.BUNKER,
        BLOCK_IDS.BUNKER_SLAB,
        BLOCK_IDS.HOLE,
        BLOCK_IDS.LEDGER
    ];
    
    // Acceptable surfaces (safe solid ground)
    const acceptableSurfaces = [
        "minecraft:grass_block",
        "minecraft:dirt",
        "minecraft:stone",
        "minecraft:cobblestone",
        "minecraft:sand",
        "minecraft:gravel",
        "minecraft:oak_planks",
        "minecraft:spruce_planks",
        "minecraft:birch_planks",
        "minecraft:jungle_planks",
        "minecraft:acacia_planks",
        "minecraft:dark_oak_planks",
        "minecraft:concrete",
        "minecraft:white_concrete",
        "minecraft:light_gray_concrete",
        "minecraft:gray_concrete",
        "minecraft:black_concrete",
        "minecraft:brown_concrete",
        "minecraft:red_concrete",
        "minecraft:orange_concrete",
        "minecraft:yellow_concrete",
        "minecraft:lime_concrete",
        "minecraft:green_concrete",
        "minecraft:cyan_concrete",
        "minecraft:light_blue_concrete",
        "minecraft:blue_concrete",
        "minecraft:purple_concrete",
        "minecraft:magenta_concrete",
        "minecraft:pink_concrete"
    ];
    
    if (perfectSurfaces.includes(surfaceType)) return 'perfect';
    if (goodSurfaces.includes(surfaceType)) return 'good';
    if (acceptableSurfaces.includes(surfaceType)) return 'acceptable';
    
    // Emergency fallback - any solid block that's not a hazard
    if (!isHazardousBlock(surfaceType) && !surfaceType.includes('water') && !surfaceType.includes('lava')) {
        return 'emergency';
    }
    
    return 'invalid';
}

/**
 * Check if a block is hazardous (water, lava, void, etc.)
 * @param {string} blockType - The block type identifier
 * @returns {boolean} True if the block is hazardous
 */
function isHazardousBlock(blockType) {
    const hazardousBlocks = [
        "minecraft:water",
        "minecraft:flowing_water",
        "minecraft:lava",
        "minecraft:flowing_lava",
        "minecraft:fire",
        "minecraft:soul_fire",
        "minecraft:magma_block",
        "minecraft:cactus",
        "minecraft:sweet_berry_bush",
        "minecraft:wither_rose",
        "minecraft:air",
        "minecraft:void_air",
        "minecraft:cave_air"
    ];
    
    return hazardousBlocks.includes(blockType);
}

/**
 * Legacy function for backward compatibility
 * @param {string} surfaceType - The surface type identifier
 * @returns {boolean} True if it's a valid golf course location
 */
function isValidLandingLocation(surfaceType) {
    const tier = getLocationValidityTier(surfaceType);
    return tier === 'perfect' || tier === 'good';
}

/**
 * Check if a block is a partial-safe block (non-full block that balls can get stuck against)
 * @param {string} blockType - The block type identifier
 * @returns {boolean} True if it's a partial-safe block
 */
function isPartialSafeBlock(blockType) {
    // Check exact matches first
    if (PARTIAL_SAFE_BLOCKS.includes(blockType)) {
        debug('verbose', `Block ${blockType} is partial-safe (exact match)`);
        return true;
    }
    
    // Check partial matches using includes()
    const isPartial = PARTIAL_SAFE_BLOCKS.some(partialId => {
        // Skip exact matches (already checked above)
        if (partialId.startsWith('minecraft:')) return false;
        
        // Check if blockType contains the partial string
        return blockType.includes(partialId);
    });
    
    if (isPartial) {
        debug('verbose', `Block ${blockType} is partial-safe (pattern match)`);
    }
    
    return isPartial;
}

/**
 * Check if ball is truly on solid ground (not mid-air) with partial block support
 * @param {Entity} golfball - The golf ball entity
 * @param {Dimension} dimension - The dimension
 * @param {boolean} allowPartialBlocks - Whether to consider partial blocks as valid support
 * @returns {boolean} True if ball is on solid ground
 */
function isBallOnSolidGround(golfball, dimension, allowPartialBlocks = false) {
    if (!golfball || !golfball.location) return false;
    
    const ballLoc = golfball.location;
    
    // Check blocks directly below the ball at multiple depths
    for (let depth = 0.1; depth <= MID_AIR_CHECK_DEPTH; depth += 0.5) {
        const checkY = Math.floor(ballLoc.y - depth);
        const block = dimension.getBlock({
            x: Math.floor(ballLoc.x),
            y: checkY,
            z: Math.floor(ballLoc.z)
        });
        
        if (!block) continue;
        
        // Skip air blocks
        if (block.typeId === "minecraft:air" || 
            block.typeId === "minecraft:void_air" || 
            block.typeId === "minecraft:cave_air") {
            continue;
        }
        
        // Skip decorative blocks
        if (isDecorativeBlock(block.typeId)) {
            continue;
        }
        
        // If we find a solid, non-air block, ball is on solid ground
        if (!isPartialSafeBlock(block.typeId)) {
            debug('verbose', `Ball ${golfball.id} found solid ground: ${block.typeId} at depth ${depth}`);
            return true; // Found solid full block
        } else if (allowPartialBlocks) {
            debug('verbose', `Ball ${golfball.id} found partial block support: ${block.typeId} at depth ${depth}`);
            return true; // Found partial block and we allow them
        }
    }
    
    debug('verbose', `Ball ${golfball.id} is mid-air - no solid ground found within ${MID_AIR_CHECK_DEPTH} blocks`);
    return false; // No solid ground found within check depth
}

/**
 * Check if ball is stuck against partial blocks (for stationary balls only)
 * @param {Entity} golfball - The golf ball entity
 * @param {Dimension} dimension - The dimension
 * @returns {boolean} True if ball is stuck against partial blocks but has solid ground nearby
 */
function isBallStuckAgainstPartialBlocks(golfball, dimension) {
    if (!golfball || !golfball.location) return false;
    
    const ballLoc = golfball.location;
    
    // Check if there are partial blocks around the ball (horizontally)
    const checkPositions = [
        { x: 1, z: 0 },   // East
        { x: -1, z: 0 },  // West
        { x: 0, z: 1 },   // South
        { x: 0, z: -1 },  // North
        { x: 1, z: 1 },   // Southeast
        { x: -1, z: 1 },  // Southwest
        { x: 1, z: -1 },  // Northeast
        { x: -1, z: -1 }  // Northwest
    ];
    
    let hasPartialBlocksNearby = false;
    
    for (const offset of checkPositions) {
        const block = dimension.getBlock({
            x: Math.floor(ballLoc.x) + offset.x,
            y: Math.floor(ballLoc.y),
            z: Math.floor(ballLoc.z) + offset.z
        });
        
        if (block && isPartialSafeBlock(block.typeId)) {
            hasPartialBlocksNearby = true;
            break;
        }
    }
    
    // If there are partial blocks nearby, check if there's solid ground below with partial block support
    if (hasPartialBlocksNearby) {
        debug('info', `Ball ${golfball.id} detected near partial blocks - checking for support`);
        const hasSupport = isBallOnSolidGround(golfball, dimension, true); // Allow partial blocks as support
        if (hasSupport) {
            debug('info', `Ball ${golfball.id} is stuck against partial blocks but has solid support`);
        } else {
            debug('warn', `Ball ${golfball.id} is stuck against partial blocks with NO solid support`);
        }
        return hasSupport;
    }
    
    return false;
}

/**
 * Check if ball is out of bounds or in hazard
 * @param {Entity} golfball - The golf ball entity
 * @param {string} surfaceType - Current surface type
 * @returns {Object} Status with isOutOfBounds, isHazard, reason, and isGolfValid
 */
export function checkBallStatus(golfball, surfaceType) {
    if (!golfball) return { isOutOfBounds: false, isHazard: false, reason: null, isGolfValid: true };
    
    // Water hazard check
    if (surfaceType === "minecraft:water" || surfaceType === "minecraft:flowing_water") {
        return {
            isOutOfBounds: false,
            isHazard: true,
            reason: "water_hazard",
            isGolfValid: false
        };
    }
    
    // Unplayable lie check (trees, leaves)
    const unplayableBlocks = [
        "minecraft:oak_log", "minecraft:spruce_log", "minecraft:birch_log",
        "minecraft:jungle_log", "minecraft:acacia_log", "minecraft:dark_oak_log",
        "minecraft:oak_leaves", "minecraft:spruce_leaves", "minecraft:birch_leaves",
        "minecraft:jungle_leaves", "minecraft:acacia_leaves", "minecraft:dark_oak_leaves"
    ];
    
    if (unplayableBlocks.includes(surfaceType)) {
        return {
            isOutOfBounds: false,
            isHazard: true,
            reason: "unplayable_lie",
            isGolfValid: false
        };
    }
    
    // Out of bounds check (non-golf course blocks)
    if (!isValidLandingLocation(surfaceType) && surfaceType.startsWith('minecraft:')) {
        return {
            isOutOfBounds: true,
            isHazard: false,
            reason: "out_of_bounds",
            isGolfValid: false
        };
    }
    
    return { isOutOfBounds: false, isHazard: false, reason: null, isGolfValid: true };
}

/**
 * Teleport ball back to valid location (simplified single location system)
 * @param {Entity} golfball - The golf ball entity
 * @param {Player} player - The player who owns the ball
 * @returns {boolean} True if teleport was successful
 */
export function teleportBallToLastValid(golfball, player) {
    if (!golfball || !player) return false;
    
    const ballId = golfball.id;
    const targetLocation = ballValidLocation.get(ballId);
    
    if (!targetLocation) {
        debug('error', `No valid location found for ball ${ballId} teleport - using emergency fallback`);
        // Emergency fallback: use player's current location
        try {
            const playerLoc = player.location;
            const emergencyLocation = {
                x: playerLoc.x,
                y: playerLoc.y,
                z: playerLoc.z,
                timestamp: Date.now(),
                validityTier: 'emergency'
            };
            
            golfball.teleport({
                x: emergencyLocation.x,
                y: emergencyLocation.y + 0.5,
                z: emergencyLocation.z
            });
            
            golfball.applyImpulse({ x: 0, y: 0, z: 0 });
            debug('warn', `Ball ${ballId} teleported to emergency location (player position)`);
            player.sendMessage("§cNo valid previous location found! Ball teleported to your position.");
            return true;
        } catch (error) {
            debug('error', `Emergency teleport failed for ball ${ballId}: ${error}`);
            player.sendMessage("§cNo valid previous location found for teleport!");
            return false;
        }
    }
    
    try {
        // Teleport ball to valid location
        golfball.teleport({
            x: targetLocation.x,
            y: targetLocation.y + 0.5, // Slightly above ground to prevent clipping
            z: targetLocation.z
        });
        
        // Reset ball velocity to prevent continued movement
        golfball.applyImpulse({ x: 0, y: 0, z: 0 });
        
        const timeSince = Math.floor((Date.now() - targetLocation.timestamp) / 1000);
        const tierInfo = targetLocation.validityTier ? ` [${targetLocation.validityTier}]` : '';
        
        debug('info', `Ball ${ballId} teleported to valid location: (${targetLocation.x.toFixed(3)}, ${targetLocation.y.toFixed(3)}, ${targetLocation.z.toFixed(3)}) [${targetLocation.validityTier}]`);
        player.sendMessage(`§aBall teleported back to valid position.`);
        player.sendMessage(`§7Location: ${targetLocation.x.toFixed(2)}, ${targetLocation.y.toFixed(2)}, ${targetLocation.z.toFixed(2)}`);
        
        return true;
    } catch (error) {
        debug('error', `Failed to teleport ball ${ballId} for ${player.name}: ${error}`);
        player.sendMessage("§cFailed to teleport ball back!");
        return false;
    }
}

/**
 * Get landing history for a ball
 * @param {string} ballId - The ball's ID
 * @returns {Array} Array of landing records
 */
export function getBallLandingHistory(ballId) {
    return ballLandingHistory.get(ballId) || [];
}

/**
 * Get valid location for a ball (single location system)
 * @param {string} ballId - The ball's ID
 * @returns {Object|null} Valid location or null
 */
export function getValidLocation(ballId) {
    return ballValidLocation.get(ballId) || null;
}

/**
 * Initialize ball tracking with actual ball spawn location
 * @param {Entity} golfball - The golf ball entity
 * @param {Player} player - The player who owns the ball
 */
export function initializeBallTracking(golfball, player) {
    if (!golfball || !player) return;
    
    const ballId = golfball.id;
    const ballLocation = getPreciseBallLocation(golfball);
    const timestamp = Date.now();
    
    if (!ballLocation) {
        debug('error', `Failed to get ball location for initialization: ${ballId}`);
        return;
    }
    
    // Set spawn time to prevent immediate validation
    ballSpawnTime.set(ballId, timestamp);
    
    // Set ball's actual spawn location as initial valid location
    ballValidLocation.set(ballId, {
        x: ballLocation.x,
        y: ballLocation.y,
        z: ballLocation.z,
        timestamp: timestamp,
        validityTier: 'perfect'
    });
    
    debug('info', `Initialized tracking for ball ${ballId} at spawn location: (${ballLocation.x.toFixed(3)}, ${ballLocation.y.toFixed(3)}, ${ballLocation.z.toFixed(3)})`);
    
    // Store the initial landing with precise tracking
    storePreciseLanding(golfball, ballLocation, 'spawn_location');
}

/**
 * Clear tracking data for a ball (when ball is removed/reset)
 * @param {string} ballId - The ball's ID
 */
export function clearBallTracking(ballId) {
    debug('info', `Clearing tracking data for ball ${ballId}`);
    ballLandingHistory.delete(ballId);
    ballCurrentPreciseLocation.delete(ballId);
    ballValidLocation.delete(ballId);
    ballSpawnTime.delete(ballId);
    ballStationaryTime.delete(ballId);
}

/**
 * Check if a block type is a decorative block that should be ignored
 * @param {string} blockType - The block type ID
 * @returns {boolean} True if it's a decorative block
 */
function isDecorativeBlock(blockType) {
    return DECOR_BLOCKS.includes(blockType);
}

/**
 * Find the actual surface below decorative blocks
 * @param {Dimension} dimension - The dimension
 * @param {Object} location - The location to check {x, y, z}
 * @param {number} maxDepth - Maximum depth to search (default: 5)
 * @returns {Object|null} Surface block info or null
 */
function findActualSurface(dimension, location, maxDepth = 5) {
    for (let depth = 0; depth <= maxDepth; depth++) {
        const checkY = Math.floor(location.y) - depth;
        const block = dimension.getBlock({
            x: Math.floor(location.x),
            y: checkY,
            z: Math.floor(location.z)
        });
        
        if (block && !isDecorativeBlock(block.typeId)) {
            return {
                blockType: block.typeId,
                blockLocation: {
                    x: Math.floor(location.x),
                    y: checkY,
                    z: Math.floor(location.z)
                }
            };
        }
    }
    return null;
}

/**
 * Continuously validate and save ball location (improved system with partial block support)
 * @param {Entity} golfball - The golf ball entity
 * @returns {boolean} True if a valid location was saved
 */
export function validateAndSaveBallLocation(golfball) {
    if (!golfball || !golfball.location) return false;
    
    const ballId = golfball.id;
    const owner = golfball.getDynamicProperty("owner");
    if (!owner) return false;
    
    // Check if ball is in grace period after spawn
    const spawnTime = ballSpawnTime.get(ballId);
    if (spawnTime && (Date.now() - spawnTime) < (SPAWN_GRACE_PERIOD * 50)) { // Convert ticks to ms
        return false; // Still in grace period
    }
    
    // Check if ball is stationary
    const velocity = golfball.getVelocity();
    const speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
    const currentTime = Date.now();
    
    if (speed > STATIONARY_VELOCITY_THRESHOLD) {
        // Ball is moving, reset stationary timer
        ballStationaryTime.delete(ballId);
        debug('verbose', `Ball ${ballId} is moving (speed: ${speed.toFixed(4)}) - validation skipped`);
        return false;
    }
    
    // Ball is stationary, track how long it's been stationary
    if (!ballStationaryTime.has(ballId)) {
        ballStationaryTime.set(ballId, currentTime);
        debug('verbose', `Ball ${ballId} became stationary (speed: ${speed.toFixed(4)})`);
    }
    
    const stationaryDuration = currentTime - ballStationaryTime.get(ballId);
    const dimension = golfball.dimension;
    
    // CRITICAL: Check if ball is actually on solid ground (not mid-air)
    let isOnSolidGround = isBallOnSolidGround(golfball, dimension);
    
    // If not on solid ground normally, check if it's stuck against partial blocks
    // Only do this check if ball has been stationary for a while
    if (!isOnSolidGround && stationaryDuration >= (STATIONARY_CHECK_TICKS * 50)) { // Convert ticks to ms
        debug('verbose', `Ball ${ballId} not on solid ground - checking partial blocks (stationary for ${(stationaryDuration/1000).toFixed(1)}s)`);
        isOnSolidGround = isBallStuckAgainstPartialBlocks(golfball, dimension);
    } else if (!isOnSolidGround) {
        debug('verbose', `Ball ${ballId} not on solid ground - waiting for stationary period (${(stationaryDuration/1000).toFixed(1)}s/${(STATIONARY_CHECK_TICKS * 50/1000).toFixed(1)}s)`);
    }
    
    if (!isOnSolidGround) {
        debug('verbose', `Ball ${ballId} validation failed - not properly supported`);
        return false; // Ball is mid-air or not properly supported
    }
    
    // Get precise location
    const preciseLocation = getPreciseBallLocation(golfball);
    if (!preciseLocation) return false;
    
    // Check block below ball
    const blockBelow = dimension.getBlock({
        x: Math.floor(preciseLocation.x),
        y: Math.floor(preciseLocation.y - 0.5),
        z: Math.floor(preciseLocation.z)
    });
    
    if (!blockBelow) return false;
    
    // Skip if block below is air or hazardous
    if (blockBelow.typeId === "minecraft:air" || isHazardousBlock(blockBelow.typeId)) {
        return false;
    }
    
    // Check if this location is valid according to golf rules
    const ballStatus = checkBallStatus(golfball, blockBelow.typeId);
    if (!ballStatus.isGolfValid) {
        return false; // Not a valid golf location
    }
    
    // SIMPLIFIED: Always update the single valid location if it's golf-valid
    // This ensures constant updates as requested
    storePreciseLanding(golfball, preciseLocation, blockBelow.typeId, true);
    
    debug('info', `Validator saved valid location for ball ${ballId}: (${preciseLocation.x.toFixed(3)}, ${preciseLocation.y.toFixed(3)}, ${preciseLocation.z.toFixed(3)}) on ${blockBelow.typeId}`);
    return true;
}

/**
 * Continuous monitoring system for ball locations (handles edge cases)
 * Call this regularly to catch balls rolling into water or other hazards
 * @param {Entity} golfball - The golf ball entity
 * @returns {boolean} True if monitoring detected and handled an issue
 */
export function continuousMonitorBall(golfball) {
    if (!golfball || !golfball.location) return false;
    
    const ballId = golfball.id;
    const owner = golfball.getDynamicProperty("owner");
    if (!owner) return false;
    
    // Skip if ball is in grace period
    const spawnTime = ballSpawnTime.get(ballId);
    if (spawnTime && (Date.now() - spawnTime) < (SPAWN_GRACE_PERIOD * 50)) {
        return false;
    }
    
    const preciseLocation = getPreciseBallLocation(golfball);
    if (!preciseLocation) return false;
    
    const dimension = golfball.dimension;
    
    // Check if ball is in water (immediate hazard detection)
    const blockAt = dimension.getBlock({
        x: Math.floor(preciseLocation.x),
        y: Math.floor(preciseLocation.y),
        z: Math.floor(preciseLocation.z)
    });
    
    if (blockAt && (blockAt.typeId === "minecraft:water" || blockAt.typeId === "minecraft:flowing_water")) {
        // Ball is in water - this is a hazard, store as invalid
        debug('warn', `Ball ${ballId} detected in water hazard at (${preciseLocation.x.toFixed(3)}, ${preciseLocation.y.toFixed(3)}, ${preciseLocation.z.toFixed(3)})`);
        storeInvalidLanding(golfball, preciseLocation, blockAt.typeId);
        return true; // Detected hazard
    }
    
    // Check if ball is stationary and on valid ground
    const velocity = golfball.getVelocity();
    const speed = Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
    
    if (speed <= STATIONARY_VELOCITY_THRESHOLD && isBallOnSolidGround(golfball, dimension)) {
        // Try to validate and save current location
        return validateAndSaveBallLocation(golfball);
    }
    
    return false;
}

/**
 * Enhanced ball landing detection with precise tracking and decorative block handling
 * @param {Entity} golfball - The golf ball entity
 * @returns {Object|null} Enhanced detection result with precise coordinates
 */
export function detectPreciseLanding(golfball) {
    if (!golfball || !golfball.location) return null;
    
    const preciseLocation = getPreciseBallLocation(golfball);
    const dimension = golfball.dimension;
    
    // Check block at ball position (for water detection)
    const blockAt = dimension.getBlock({
        x: Math.floor(preciseLocation.x),
        y: Math.floor(preciseLocation.y),
        z: Math.floor(preciseLocation.z)
    });
    
    // Prioritize water detection at ball position
    if (blockAt && (blockAt.typeId === "minecraft:water" || blockAt.typeId === "minecraft:flowing_water")) {
        return {
            blockType: blockAt.typeId,
            blockLocation: {
                x: Math.floor(preciseLocation.x),
                y: Math.floor(preciseLocation.y),
                z: Math.floor(preciseLocation.z)
            },
            preciseLocation: preciseLocation,
            ballStatus: checkBallStatus(golfball, blockAt.typeId)
        };
    }
    
    // Check block below (for surface detection)
    const blockBelow = dimension.getBlock({
        x: Math.floor(preciseLocation.x),
        y: Math.floor(preciseLocation.y - 0.5),
        z: Math.floor(preciseLocation.z)
    });
    
    let surfaceInfo = null;
    
    // If block below exists and is not decorative, use it
    if (blockBelow && !isDecorativeBlock(blockBelow.typeId)) {
        surfaceInfo = {
            blockType: blockBelow.typeId,
            blockLocation: {
                x: Math.floor(preciseLocation.x),
                y: Math.floor(preciseLocation.y - 0.5),
                z: Math.floor(preciseLocation.z)
            }
        };
    } else {
        // Block below is decorative or doesn't exist, search deeper
        surfaceInfo = findActualSurface(dimension, {
            x: preciseLocation.x,
            y: preciseLocation.y - 0.5,
            z: preciseLocation.z
        });
    }
    
    // Fallback to block at position if no surface found below
    if (!surfaceInfo && blockAt && !isDecorativeBlock(blockAt.typeId)) {
        surfaceInfo = {
            blockType: blockAt.typeId,
            blockLocation: {
                x: Math.floor(preciseLocation.x),
                y: Math.floor(preciseLocation.y),
                z: Math.floor(preciseLocation.z)
            }
        };
    }
    
    if (!surfaceInfo) return null;
    
    return {
        blockType: surfaceInfo.blockType,
        blockLocation: surfaceInfo.blockLocation,
        preciseLocation: preciseLocation, // Include precise coordinates
        ballStatus: checkBallStatus(golfball, surfaceInfo.blockType)
    };
}
