// Golf Block Detection System
// Detects when golf balls land on specific blocks and announces to players

import { world, system } from "@minecraft/server";
import { 
    storePreciseLanding, 
    storeInvalidLanding,
    detectPreciseLanding, 
    teleportBallToLastValid, 
    clearBallTracking,
    hasMovedSignificantly,
    validateAndSaveBallLocation,
    continuousMonitorBall
} from './precise_tracking.js';
import { GOLFBALL_SPAWN_EGG_ID, PHYSICS } from './config.js';
import { debug } from './utils.js';

// Block identifiers for detection
export const BLOCK_IDS = {
    CHECKERED_GRASS: "block_jesu:dss_gep_checkered_grass",
    CHECKERED_GRASS_SLAB: "block_jesu:dss_gep_checkered_grass_slab",
    BUNKER: "block_jesu:dss_gep_sand_bunker",
    BUNKER_SLAB: "block_jesu:dss_gep_sand_bunker_slab",
    ROUGH: "block_jesu:dss_gep_rough_grass",
    ROUGH_OUTLINED: "block_jesu:dss_gep_grass_02",
    ROUGH_SLAB: "block_jesu:dss_gep_grass_slab",
    ROUGH_GRASS: "block_jesu:dss_gep_grass",
    GREEN: "block_jesu:dss_gep_green_grass",
    GREEN_SLAB: "block_jesu:dss_gep_green_grass_slab",
    HOLE: "block_jesu:dss_gep_hole",
    LEDGER: "block_jesu:dss_gep_ledger",
    FAIRWAY_LIGHT: "block_jesu:dss_gep_fairway_grass_light_shade",
    FAIRWAY_LIGHT_SLAB: "block_jesu:dss_gep_fairway_light_slab",
    FAIRWAY_MID: "block_jesu:dss_gep_fairway_grass_mid_shade",
    FAIRWAY_MID_SLAB: "block_jesu:dss_gep_fairway_mid_slab"
};

// Water block types for hazard detection
const WATER_BLOCKS = [
    "minecraft:water",
    "minecraft:flowing_water",
    "minecraft:kelp",
    "minecraft:waterlily"
];

// Tree blocks and leaves for unplayable lie detection
const UNPLAYABLE_BLOCKS = [
    // Tree logs
    "minecraft:oak_log",
    "minecraft:spruce_log",
    "minecraft:birch_log",
    "minecraft:jungle_log",
    "minecraft:acacia_log",
    "minecraft:dark_oak_log",
    "minecraft:mangrove_log",
    "minecraft:cherry_log",
    "minecraft:warped_stem",
    "minecraft:crimson_stem",
    
    // Leaves
    "minecraft:oak_leaves",
    "minecraft:spruce_leaves",
    "minecraft:birch_leaves",
    "minecraft:jungle_leaves",
    "minecraft:acacia_leaves",
    "minecraft:dark_oak_leaves",
    "minecraft:mangrove_leaves",
    "minecraft:cherry_leaves",
    "minecraft:azalea_leaves",
    "minecraft:flowering_azalea_leaves",
    "minecraft:warped_wart_block",
    "minecraft:nether_wart_block",
    "minecraft:shroomlight"
];

export const DECOR_BLOCKS = [
    "block_jesu:golf_sign_1",
    "block_jesu:lighthouse_decor",
    "block_jesu:mini_bridge",
    "block_jesu:sign_1",
    "block_jesu:small_hill",
    "block_jesu:tee",
    "dss_gep:dss_gep_golf_bag",
    "minecraft:air",
    "block_jesu:dss_gep_stone_block",
    "block_jesu:dss_gep_stone_slab",
    "block_jesu:dss_gep_rocks_block",
    "block_jesu:dss_gep_rocks_slab"
];

// Landing detection state to prevent duplicate announcements
const landingAnnouncements = new Map();
const ANNOUNCEMENT_TIMEOUT = 5000; // 5 seconds in milliseconds

// Track ball hit state to only announce after actual hits
const ballHitState = new Map();

/**
 * Detect what block a golf ball has landed on
 * @param {Entity} golfball - The golf ball entity
 * @returns {Object|null} Block detection result with type and position
 */
export function detectLandingBlock(golfball) {
    if (!golfball || !golfball.location) return null;
    
    const location = golfball.location;
    const dimension = golfball.dimension;
    
    // Check the block at the golf ball's current position for water detection
    const blockAt = dimension.getBlock({
        x: Math.floor(location.x),
        y: Math.floor(location.y),
        z: Math.floor(location.z)
    });
    
    if (!blockAt) return null;
    
    // Check the block below for the actual surface
    const blockBelow = dimension.getBlock({
        x: Math.floor(location.x),
        y: Math.floor(location.y - 1),
        z: Math.floor(location.z)
    });
    
    // Prioritize water detection at ball position
    if (isWaterBlock(blockAt.typeId)) {
        return {
            blockType: blockAt.typeId,
            blockLocation: {
                x: Math.floor(location.x),
                y: Math.floor(location.y),
                z: Math.floor(location.z)
            }
        };
    }
    
    // Use the block below for surface detection (golf ball is on top of this block)
    if (blockBelow) {
        return {
            blockType: blockBelow.typeId,
            blockLocation: {
                x: Math.floor(location.x),
                y: Math.floor(location.y - 1),
                z: Math.floor(location.z)
            }
        };
    } else if (blockAt) {
        // Fallback to block at position if no block below
        return {
            blockType: blockAt.typeId,
            blockLocation: {
                x: Math.floor(location.x),
                y: Math.floor(location.y),
                z: Math.floor(location.z)
            }
        };
    }
    
    return null;
}

/**
 * Announce golf ball landing to all players
 * @param {string} playerName - Name of the player who owns the ball
 * @param {string} blockType - Type of block landed on
 * @param {Object} location - Landing location {x, y, z}
 */
export function announceLanding(playerName, blockType, location) {
    const announcementKey = `${playerName}_${location.x}_${location.y}_${location.z}`;
    
    // Prevent duplicate announcements for the same landing
    if (landingAnnouncements.has(announcementKey)) {
        return;
    }
    
    // Set announcement timeout (5 seconds)
    landingAnnouncements.set(announcementKey, Date.now());
    
    // Schedule cleanup using system.runTimeout
    system.runTimeout(() => {
        landingAnnouncements.delete(announcementKey);
    }, 100); // 100 ticks = 5 seconds
    
    let message = "";
    
    switch (blockType) {
        case "water_hazard":
            message = `§b${playerName}'s golf ball landed in a water hazard!`;
            break;
        case "out_of_bounds":
            message = `§c${playerName}'s golf ball is out of bounds!`;
            break;
        case BLOCK_IDS.CHECKERED_GRASS || BLOCK_IDS.CHECKERED_GRASS_SLAB:
            message = `§6${playerName}'s golf ball landed on tee area!`;
            break;
        case BLOCK_IDS.BUNKER || BLOCK_IDS.BUNKER_SLAB:
            message = `§e${playerName}'s golf ball landed in a bunker!`;
            break;
        case BLOCK_IDS.ROUGH || BLOCK_IDS.ROUGH_OUTLINED || BLOCK_IDS.ROUGH_SLAB || BLOCK_IDS.ROUGH_GRASS:
            message = `§6${playerName}'s golf ball landed in the rough!`;
            break;
        case BLOCK_IDS.GREEN || BLOCK_IDS.GREEN_SLAB:
            message = `§a${playerName}'s golf ball landed on the green!`;
            break;
        case BLOCK_IDS.FAIRWAY_LIGHT || BLOCK_IDS.FAIRWAY_LIGHT_SLAB:
        case BLOCK_IDS.FAIRWAY_MID || BLOCK_IDS.FAIRWAY_MID_SLAB:
            message = `§2${playerName}'s golf ball landed on the fairway!`;
            break;
        case BLOCK_IDS.HOLE:
            // Don't announce hole landings - handled by main scoring system
            return;
        case "unplayable_lie":
            message = `§c${playerName}'s golf ball landed on an unplayable lie!`;
            break;
        default:
            // Optional: announce other block types with cleaned names
            const cleanName = blockType
                .replace('block_jesu:dss_gep_', '')
                .replace('minecraft:', '')
                .replace(/_/g, ' ');
            message = `§f${playerName}'s golf ball landed on ${cleanName}`;
            break;
    }
    
    // Announce to all players in the dimension
    world.getDimension("overworld").getEntities({
        type: "minecraft:player"
    }).forEach(player => {
        player.sendMessage(message);
    });
}

/**
 * Check if a golf ball has stopped moving (landed) - stricter threshold for announcements
 * @param {Entity} golfball - The golf ball entity
 * @returns {boolean} True if the ball has stopped moving
 */
export function hasBallLanded(golfball) {
    if (!golfball) return false;
    
    try {
        const velocity = golfball.getVelocity();
        const speed = Math.sqrt(
            velocity.x * velocity.x + 
            velocity.y * velocity.y + 
            velocity.z * velocity.z
        );
        
        // Use stricter threshold for landing announcements to prevent early announcements
        // when hitting partial block walls. This should be lower than the validation threshold.
        const landingThreshold = (PHYSICS?.STATIONARY_VELOCITY_THRESHOLD || 0.05) * 0.5; // Half the validation threshold
        const hasLanded = speed < landingThreshold;
        
        if (hasLanded) {
            debug('verbose', `Ball ${golfball.id} considered landed (speed: ${speed.toFixed(4)} < ${landingThreshold.toFixed(4)})`);
        }
        
        return hasLanded;
    } catch (error) {
        return false;
    }
}

/**
 * Check if a block type is water
 * @param {string} blockType - The block type ID
 * @returns {boolean} True if it's a water block
 */
function isWaterBlock(blockType) {
    return WATER_BLOCKS.includes(blockType);
}

/**
 * Check if a block type represents an unplayable lie (tree blocks/leaves)
 * @param {string} blockType - The block type ID
 * @returns {boolean} True if it's an unplayable lie
 */
function isUnplayableLie(blockType) {
    return UNPLAYABLE_BLOCKS.includes(blockType);
}


// Track when balls first become stationary to prevent immediate announcements
const ballLandingDelayTime = new Map();
const LANDING_ANNOUNCEMENT_DELAY = 30; // 30 ticks (1.5 seconds) delay before announcing

/**
 * Enhanced ball processing with continuous monitoring integration
 * @param {Entity} golfball - The golf ball entity to check
 */
export function processEnhancedBallTracking(golfball) {
    if (!golfball || !golfball.location) return;
    
    const ballId = golfball.id;
    
    // First, run continuous monitoring to catch edge cases
    const monitorResult = continuousMonitorBall(golfball);
    if (monitorResult) {
        debug('info', `Continuous monitor detected issue for ball ${ballId}`);
    }
    
    // Then run validation to update valid locations
    const validationResult = validateAndSaveBallLocation(golfball);
    if (validationResult) {
        debug('verbose', `Ball ${ballId} location validated and saved`);
    }
    
    // Handle landing announcements with delay to prevent early announcements
    if (hasBallLanded(golfball)) {
        const currentTick = system.currentTick;
        
        // Check if this is the first time we detected the ball as landed
        if (!ballLandingDelayTime.has(ballId)) {
            ballLandingDelayTime.set(ballId, currentTick);
            debug('verbose', `Ball ${ballId} first detected as landed - starting delay timer`);
            return; // Don't process landing yet
        }
        
        // Check if enough time has passed since first detection
        const landingTime = ballLandingDelayTime.get(ballId);
        const ticksSinceLanding = currentTick - landingTime;
        
        if (ticksSinceLanding >= LANDING_ANNOUNCEMENT_DELAY) {
            // Enough time has passed, process the landing
            processBallLanding(golfball);
            ballLandingDelayTime.delete(ballId); // Clean up
        } else {
            debug('verbose', `Ball ${ballId} landing delayed (${ticksSinceLanding}/${LANDING_ANNOUNCEMENT_DELAY} ticks)`);
        }
    } else {
        // Ball is moving again, reset the landing delay
        if (ballLandingDelayTime.has(ballId)) {
            debug('verbose', `Ball ${ballId} started moving again - resetting landing delay`);
            ballLandingDelayTime.delete(ballId);
        }
    }
}

/**
 * Process golf ball landing detection with enhanced precision tracking
 * @param {Entity} golfball - The golf ball entity to check
 */
export function processBallLanding(golfball) {
    if (!hasBallLanded(golfball)) return;
    
    const ballId = golfball.id;
    const hitState = ballHitState.get(ballId);
    
    // Only process if ball was actually hit and hasn't been announced yet
    if (!hitState || hitState.announced) return;
    
    // Use enhanced precise detection
    const detection = detectPreciseLanding(golfball);
    if (!detection) return;
    
    const owner = golfball.getDynamicProperty("owner");
    if (!owner) return;
    
    // Handle hazards and out-of-bounds using enhanced detection
    const ballStatus = detection.ballStatus;
    
    // CRITICAL FIX: Use different storage methods based on golf validity
    // This prevents overwriting valid locations with invalid ones
    if (ballStatus.isGolfValid) {
        // Store as valid location - this will update ballValidLocation
        storePreciseLanding(golfball, detection.preciseLocation, detection.blockType, true);
        debug('info', `Ball ${ballId} landing processed as VALID on ${detection.blockType}`);
    } else {
        // Store as invalid location - this will NOT overwrite ballValidLocation
        storeInvalidLanding(golfball, detection.preciseLocation, detection.blockType);
        debug('warn', `Ball ${ballId} landing processed as INVALID on ${detection.blockType} - ${ballStatus.reason}`);
    }
    const player = world.getDimension("overworld").getEntities({
        type: "minecraft:player"
    }).find(p => p.name === owner);
    
    if (!player) return;
    
    if (ballStatus.isHazard || ballStatus.isOutOfBounds) {
        // Announce the issue
        let announcementType = ballStatus.reason;
        if (ballStatus.isOutOfBounds) {
            announcementType = "out_of_bounds";
        }
        
        announceLanding(owner, announcementType, detection.blockLocation);
        
        // Schedule teleport back to last valid location after 3 seconds
        system.runTimeout(() => {
            try {
                const teleportSuccess = teleportBallToLastValid(golfball, player);
                if (!teleportSuccess) {
                    debug('error', `Teleport failed for ball ${ballId} - providing new ball`);
                    throw new Error('Teleport failed');
                }
                debug('info', `Ball ${ballId} successfully teleported back from ${ballStatus.reason}`);
            } catch (error) {
                debug('error', `Teleport error for ball ${ballId}: ${error.message}`);
                // Clean up and provide new ball
                try {
                    if (golfball.isValid()) golfball.remove();
                } catch (removeError) {
                    debug('warn', `Failed to remove invalid ball ${ballId}: ${removeError.message}`);
                }
                clearBallTracking(ballId);
                player.runCommand(`/give @s ${GOLFBALL_SPAWN_EGG_ID}`);
                player.sendMessage("§cNo valid location found - new ball provided!");
            }
        }, 60); // 60 ticks = 3 seconds
    } else {
        // Check for golf course features in priority order
        let detectedType = detection.blockType;
        
        // Skip air blocks
        if (detection.blockType === 'minecraft:air') {
            return;
        }
        
        // Map detected block to the correct category
        const blockType = detection.blockType.toLowerCase();
        
        // console.log(`DEBUG: Raw block type detected: ${detection.blockType}`);
        // console.log(`DEBUG: Lowercase block type: ${blockType}`);
        
        // Check for all custom golf course blocks by name matching

        if (detection.blockType === BLOCK_IDS.FAIRWAY_LIGHT || detection.blockType === BLOCK_IDS.FAIRWAY_MID || detection.blockType === BLOCK_IDS.FAIRWAY_LIGHT_SLAB || detection.blockType === BLOCK_IDS.FAIRWAY_MID_SLAB) {
            detectedType = BLOCK_IDS.FAIRWAY_LIGHT;
        } else if (detection.blockType === BLOCK_IDS.CHECKERED_GRASS || detection.blockType === BLOCK_IDS.CHECKERED_GRASS_SLAB) {
            detectedType = BLOCK_IDS.CHECKERED_GRASS;
        } else if (detection.blockType === BLOCK_IDS.BUNKER || detection.blockType === BLOCK_IDS.BUNKER_SLAB) {
            detectedType = BLOCK_IDS.BUNKER;
        } else if (detection.blockType === BLOCK_IDS.ROUGH || detection.blockType === BLOCK_IDS.ROUGH_SLAB || detection.blockType === BLOCK_IDS.ROUGH_OUTLINED || detection.blockType === BLOCK_IDS.ROUGH_GRASS) {
            detectedType = BLOCK_IDS.ROUGH;
        } else if (detection.blockType === BLOCK_IDS.GREEN || detection.blockType === BLOCK_IDS.GREEN_SLAB) {
            detectedType = BLOCK_IDS.GREEN;
        } else if (blockType.startsWith('minecraft:')) {
            detectedType = "out_of_bounds";
        } else if (UNPLAYABLE_BLOCKS.includes(blockType)) {
            detectedType = "unplayable_lie";
        } else {
            detectedType = detection.blockType;
        }
        
        // console.log(`Detected block type: ${detection.blockType} -> ${detectedType}`);
        
        announceLanding(owner, detectedType, detection.blockLocation);
    }
    
    ballHitState.set(ballId, { ...hitState, announced: true });
}

/**
 * Mark a golf ball as having been hit
 * @param {Entity} golfball - The golf ball entity
 */
export function markBallAsHit(golfball) {
    if (!golfball) return;
    
    const ballId = golfball.id;
    ballHitState.set(ballId, {
        hitTime: Date.now(),
        announced: false
    });
}

/**
 * Clean up old ball hit states and tracking data
 */
export function cleanupBallHitStates() {
    const now = Date.now();
    for (const [ballId, hitState] of ballHitState.entries()) {
        // Clean up after 30 seconds of inactivity
        if (now - hitState.hitTime > 30000) {
            ballHitState.delete(ballId);
            ballLandingDelayTime.delete(ballId); // Clean up landing delay tracking
            // Also clean up precise tracking data for inactive balls
            clearBallTracking(ballId);
        }
    }
}

// Cleanup old announcement records periodically
// This will be handled by the main game loop in main.js
// No need for separate interval here
