// Golf Hole Detection Validation System
// Provides enhanced hole detection with multiple validation methods

import { HOLE_BLOCK_ID, SCORING } from './config.js';
import { calculateDistance } from './utils.js';

// Validation state tracking
const ballHoleProximity = new Map(); // Track balls near holes
const holeValidationHistory = new Map(); // Track validation attempts

/**
 * Enhanced hole detection with multiple validation methods
 * @param {Entity} golfball - The golf ball entity
 * @param {Dimension} dimension - The dimension the ball is in
 * @returns {Object|null} Hole detection result or null if not in hole
 */
export function validateHoleDetection(golfball, dimension) {
    const location = golfball.location;
    const ballId = golfball.id;
    
    // Method 1: Original detection (block directly below)
    const originalResult = checkOriginalMethod(location, dimension);
    
    // Method 1.5: Ball inside hole detection (ball occupying same space as hole)
    const insideHoleResult = checkBallInsideHole(location, dimension);
    
    // Method 2: Proximity-based detection (within hole area)
    const proximityResult = checkProximityMethod(location, dimension);
    
    // Method 3: Multi-point detection (check multiple points around ball)
    const multiPointResult = checkMultiPointMethod(location, dimension);
    
    // Method 4: Historical validation (track ball movement near holes)
    const historicalResult = checkHistoricalMethod(ballId, location, dimension);
    
    // Combine results with weighted scoring
    const validationScore = calculateValidationScore({
        original: originalResult,
        insideHole: insideHoleResult,
        proximity: proximityResult,
        multiPoint: multiPointResult,
        historical: historicalResult
    });
    
    // Return hole detection if validation score meets threshold
    if (validationScore.isValid) {
        return {
            method: validationScore.primaryMethod,
            confidence: validationScore.confidence,
            holeLocation: validationScore.holeLocation,
            ballSpeed: calculateBallSpeed(golfball)
        };
    }
    
    return null;
}

/**
 * Ball inside hole detection (ball occupying same space as hole block)
 */
function checkBallInsideHole(location, dimension) {
    try {
        // Check if ball is occupying the same block space as a hole
        const ballBlockPos = {
            x: Math.floor(location.x),
            y: Math.floor(location.y),
            z: Math.floor(location.z)
        };
        
        const blockAtBallPosition = dimension.getBlock(ballBlockPos);
        
        if (blockAtBallPosition?.typeId === HOLE_BLOCK_ID) {
            return {
                detected: true,
                confidence: 1.0, // Highest confidence - ball is literally inside the hole
                holeLocation: ballBlockPos
            };
        }
        
        // Also check one block below in case the ball is floating slightly above
        const blockBelowBall = dimension.getBlock({
            x: ballBlockPos.x,
            y: ballBlockPos.y - 1,
            z: ballBlockPos.z
        });
        
        if (blockBelowBall?.typeId === HOLE_BLOCK_ID) {
            return {
                detected: true,
                confidence: 0.9, // Very high confidence
                holeLocation: {
                    x: ballBlockPos.x,
                    y: ballBlockPos.y - 1,
                    z: ballBlockPos.z
                }
            };
        }
    } catch (error) {
        // Silently handle block access errors
    }
    
    return { detected: false, confidence: 0 };
}

/**
 * Original hole detection method (block directly below ball)
 */
function checkOriginalMethod(location, dimension) {
    try {
        const blockBelow = dimension.getBlock({ 
            x: Math.floor(location.x), 
            y: Math.floor(location.y - 0.5), 
            z: Math.floor(location.z) 
        });
        
        if (blockBelow?.typeId === HOLE_BLOCK_ID) {
            return {
                detected: true,
                confidence: 1.0,
                holeLocation: {
                    x: Math.floor(location.x),
                    y: Math.floor(location.y - 0.5),
                    z: Math.floor(location.z)
                }
            };
        }
    } catch (error) {
        // Silently handle block access errors
    }
    
    return { detected: false, confidence: 0 };
}

/**
 * Proximity-based detection (check area around ball)
 */
function checkProximityMethod(location, dimension) {
    const searchRadius = 1; // Check 1 block radius around ball
    let closestHole = null;
    let minDistance = Infinity;
    
    try {
        // Check blocks in a small radius around the ball
        for (let x = -searchRadius; x <= searchRadius; x++) {
            for (let z = -searchRadius; z <= searchRadius; z++) {
                for (let y = -1; y <= 0; y++) { // Check below and at ball level
                    const checkPos = {
                        x: Math.floor(location.x) + x,
                        y: Math.floor(location.y) + y,
                        z: Math.floor(location.z) + z
                    };
                    
                    const block = dimension.getBlock(checkPos);
                    if (block?.typeId === HOLE_BLOCK_ID) {
                        const distance = calculateDistance(location, {
                            x: checkPos.x + 0.5,
                            y: checkPos.y + 1,
                            z: checkPos.z + 0.5
                        });
                        
                        if (distance < minDistance) {
                            minDistance = distance;
                            closestHole = checkPos;
                        }
                    }
                }
            }
        }
        
        if (closestHole && minDistance <= 0.8) { // Within 0.8 blocks of hole center
            return {
                detected: true,
                confidence: Math.max(0.3, 1.0 - (minDistance / 0.8)),
                holeLocation: closestHole,
                distance: minDistance
            };
        }
    } catch (error) {
        // Silently handle block access errors
    }
    
    return { detected: false, confidence: 0 };
}

/**
 * Multi-point detection (check multiple points around ball entity)
 */
function checkMultiPointMethod(location, dimension) {
    const checkPoints = [
        { x: 0, z: 0 },     // Center
        { x: 0.3, z: 0 },   // East
        { x: -0.3, z: 0 },  // West
        { x: 0, z: 0.3 },   // South
        { x: 0, z: -0.3 },  // North
    ];
    
    let detectionCount = 0;
    let holeLocation = null;
    
    try {
        for (const offset of checkPoints) {
            const checkPos = {
                x: Math.floor(location.x + offset.x),
                y: Math.floor(location.y - 0.5),
                z: Math.floor(location.z + offset.z)
            };
            
            const block = dimension.getBlock(checkPos);
            if (block?.typeId === HOLE_BLOCK_ID) {
                detectionCount++;
                if (!holeLocation) {
                    holeLocation = checkPos;
                }
            }
        }
        
        if (detectionCount > 0) {
            return {
                detected: true,
                confidence: Math.min(1.0, detectionCount / checkPoints.length + 0.2),
                holeLocation: holeLocation,
                pointsDetected: detectionCount
            };
        }
    } catch (error) {
        // Silently handle block access errors
    }
    
    return { detected: false, confidence: 0 };
}

/**
 * Historical validation (track ball movement patterns near holes)
 */
function checkHistoricalMethod(ballId, location, dimension) {
    const currentTime = Date.now();
    
    // Get or create history for this ball
    if (!holeValidationHistory.has(ballId)) {
        holeValidationHistory.set(ballId, {
            positions: [],
            nearHoleTime: 0,
            lastUpdate: currentTime
        });
    }
    
    const history = holeValidationHistory.get(ballId);
    
    // Add current position to history
    history.positions.push({
        location: { ...location },
        timestamp: currentTime
    });
    
    // Keep only recent positions (last 3 seconds)
    history.positions = history.positions.filter(pos => 
        currentTime - pos.timestamp < 3000
    );
    
    // Check if ball has been near a hole recently
    let nearHoleCount = 0;
    let detectedHole = null;
    
    try {
        for (const pos of history.positions) {
            const proximityCheck = checkProximityMethod(pos.location, dimension);
            if (proximityCheck.detected) {
                nearHoleCount++;
                if (!detectedHole) {
                    detectedHole = proximityCheck.holeLocation;
                }
            }
        }
        
        // If ball has been near hole for multiple position checks
        if (nearHoleCount >= 2) {
            return {
                detected: true,
                confidence: Math.min(0.8, nearHoleCount / history.positions.length),
                holeLocation: detectedHole,
                historicalEvidence: nearHoleCount
            };
        }
    } catch (error) {
        // Silently handle errors
    }
    
    return { detected: false, confidence: 0 };
}

/**
 * Calculate validation score from all methods
 */
function calculateValidationScore(results) {
    const weights = {
        insideHole: 0.5,  // Highest weight - ball is literally inside the hole
        original: 0.3,    // High weight for original method
        proximity: 0.15,  // Medium weight for proximity
        multiPoint: 0.1,  // Lower weight for multi-point
        historical: 0.05  // Lowest weight for historical
    };
    
    let totalScore = 0;
    let primaryMethod = 'none';
    let bestConfidence = 0;
    let holeLocation = null;
    
    // Calculate weighted score
    for (const [method, result] of Object.entries(results)) {
        if (result.detected) {
            const weightedScore = result.confidence * weights[method];
            totalScore += weightedScore;
            
            if (result.confidence > bestConfidence) {
                bestConfidence = result.confidence;
                primaryMethod = method;
                holeLocation = result.holeLocation;
            }
        }
    }
    
    // Validation passes if total score exceeds threshold
    const isValid = totalScore >= 0.25; // Lowered threshold for better detection
    
    return {
        isValid,
        confidence: totalScore,
        primaryMethod,
        holeLocation
    };
}

/**
 * Calculate ball speed
 */
function calculateBallSpeed(golfball) {
    try {
        const velocity = golfball.getVelocity();
        return Math.sqrt(velocity.x * velocity.x + velocity.y * velocity.y + velocity.z * velocity.z);
    } catch (error) {
        return 0;
    }
}

/**
 * Enhanced speed validation with dynamic thresholds
 */
export function validateBallSpeed(speed, detectionMethod) {
    // Different speed thresholds based on detection method
    const thresholds = {
        insideHole: SCORING.HOLE_SPEED_THRESHOLD * 3.0,  // 1.5 (most lenient - ball is inside hole)
        original: SCORING.HOLE_SPEED_THRESHOLD,          // 0.5 (strict)
        proximity: SCORING.HOLE_SPEED_THRESHOLD * 1.5,   // 0.75 (more lenient)
        multiPoint: SCORING.HOLE_SPEED_THRESHOLD * 1.2,  // 0.6 (slightly lenient)
        historical: SCORING.HOLE_SPEED_THRESHOLD * 2.0   // 1.0 (lenient)
    };
    
    const threshold = thresholds[detectionMethod] || SCORING.HOLE_SPEED_THRESHOLD;
    return speed < threshold;
}

/**
 * Cleanup validation history for removed balls
 */
export function cleanupValidationHistory() {
    const currentTime = Date.now();
    
    for (const [ballId, history] of holeValidationHistory.entries()) {
        // Remove history for balls not updated in last 30 seconds
        if (currentTime - history.lastUpdate > 30000) {
            holeValidationHistory.delete(ballId);
        }
    }
    
    // Also cleanup proximity tracking
    for (const [ballId, data] of ballHoleProximity.entries()) {
        if (currentTime - data.lastUpdate > 30000) {
            ballHoleProximity.delete(ballId);
        }
    }
}
