/**
 * Neon Ping-Pong Bot AI Module
 * Copyright (c) 2026 Wspólnie - wspolpraca@togethere.cloud
 * All rights reserved. Unauthorized copying, distribution, or modification is prohibited.
 * 
 * AI dla bota w różnych poziomach trudności
 * Skalowalne dla przyszłych ulepszeń
 */

(function() {
    'use strict';

class BotAI {
    constructor() {
        // Konfiguracja dla różnych poziomów trudności
        this.difficulties = {
            easy: {
                maxSpeed: 2.7,
                reactionDelay: 11,
                accuracy: 0.45,
                predictionEnabled: true,
                predictionStrength: 0.21,
                returnToCenter: true,
                aimStrength: 0.018,
                gain: 0.072,
                deadzone: 17,
                closeRangeBoost: 1.012
            },
            medium: {
                maxSpeed: 4.2,
                reactionDelay: 7,
                accuracy: 0.53,
                predictionEnabled: true,
                predictionStrength: 0.41,
                returnToCenter: true,
                aimStrength: 0.072,
                gain: 0.114,
                deadzone: 11,
                closeRangeBoost: 1.06
            },
            hard: {
                maxSpeed: 5.2,
                reactionDelay: 5,
                accuracy: 0.57,
                predictionEnabled: true,
                predictionStrength: 0.52,
                returnToCenter: true,
                aimStrength: 0.108,
                gain: 0.132,
                deadzone: 10,
                closeRangeBoost: 1.09
            },
            extreme: {
                maxSpeed: 7.5,
                reactionDelay: 2,
                accuracy: 0.92,
                predictionEnabled: true,
                predictionStrength: 0.88,
                returnToCenter: true,
                aimStrength: 0.185,
                gain: 0.195,
                deadzone: 3,
                closeRangeBoost: 1.22
            }
        };
        
        this.reactionCounter = 0;
        this._cachedTargetY = null;
    }
    
    /**
     * Aktualizacja pozycji bota
     * @param {Object} bot - Obiekt bota z pozycją i wymiarami
     * @param {Object} ball - Obiekt piłki z pozycją i prędkością
     * @param {String} difficulty - Poziom trudności ('easy', 'medium', 'hard')
     */
    update(bot, ball, difficulty = 'easy', canvasHeight = 450) {
        let config = this.difficulties[difficulty];
        if (!config) {
            console.warn(`Nieznany poziom trudności: ${difficulty}. Używam 'easy'.`);
            config = this.difficulties.easy;
        }

        // Opóźnienie reakcji: bot aktualizuje "cel" co N klatek (żeby nie był nadludzki)
        this.reactionCounter++;
        if (this.reactionCounter >= config.reactionDelay || this._cachedTargetY === null) {
            this.reactionCounter = 0;
            this._cachedTargetY = this.computeTargetY(bot, ball, config, canvasHeight);
        }

        const botCenter = bot.y + bot.height / 2;
        const desiredCenter = this._cachedTargetY;

        // Sterowanie płynne: prędkość zależy od dystansu
        const error = desiredCenter - botCenter;
        const deadzone = Number.isFinite(config.deadzone) ? config.deadzone : 6;
        if (Math.abs(error) <= deadzone) {
            return;
        }

        // Gain: jak agresywnie goni cel; większe na trudniejszych
        const gain = Number.isFinite(config.gain) ? config.gain : (config.predictionEnabled ? 0.22 : 0.18);

        // Mały boost prędkości, gdy piłka jest już blisko bota (na wyższych trudnościach)
        let maxSpeed = config.maxSpeed;
        if (ball && typeof ball.dx === 'number' && ball.dx > 0) {
            const distanceToBotX = (bot.x - ball.x);
            if (Number.isFinite(distanceToBotX) && distanceToBotX < 220) {
                const boost = Number.isFinite(config.closeRangeBoost) ? config.closeRangeBoost : 1.0;
                maxSpeed = maxSpeed * boost;
            }
        }

        const step = this.clamp(error * gain, -maxSpeed, maxSpeed);
        bot.y += step;

        // Bezpieczny clamp w canvas (uwzględnia rozmiar paletki)
        if (bot.y < 0) bot.y = 0;
        if (bot.y + bot.height > canvasHeight) bot.y = canvasHeight - bot.height;
    }

    computeTargetY(bot, ball, config, canvasHeight) {
        const radius = ball.radius || 0;
        const minY = radius;
        const maxY = Math.max(minY, canvasHeight - radius);

        // Gdy piłka leci od bota (w lewo), bot wraca w okolice środka
        if (ball.dx <= 0) {
            if (config.returnToCenter) {
                const center = canvasHeight / 2;
                return this.clamp(center, minY, maxY);
            }
            return this.clamp(ball.y, minY, maxY);
        }

        // Predykcja: gdzie piłka przetnie linię bota (z odbiciami)
        let targetY = ball.y;
        if (config.predictionEnabled) {
            targetY = this.predictBallPosition(ball, bot, canvasHeight, config.predictionStrength);
        }

        // Strategia: na trudniejszych poziomach lekko celuje w krawędzie (żeby wybijać pod kątem)
        if (config.aimStrength && config.aimStrength > 0) {
            const direction = ball.dy >= 0 ? 1 : -1;
            targetY += direction * bot.height * config.aimStrength;
        }

        // Realistyczny błąd (skalowany paletką, nie stałe 100px)
        const maxError = (1 - config.accuracy) * bot.height * 0.9;
        targetY += (Math.random() - 0.5) * 2 * maxError;

        return this.clamp(targetY, minY, maxY);
    }
    
    /**
     * Przewiduje pozycję piłki gdy dotrze do bota
     * @param {Object} ball - Obiekt piłki
     * @param {Object} bot - Obiekt bota
     * @param {Number} strength - Siła predykcji (0-1)
     * @returns {Number} Przewidywana pozycja Y
     */
    predictBallPosition(ball, bot, canvasHeight, strength) {
        // Czas dotarcia do osi bota (dx > 0 gwarantowane wyżej)
        const dx = ball.dx;
        if (dx <= 0) return ball.y;

        const distanceToBotX = (bot.x - ball.x);
        const timeToReach = distanceToBotX / dx;
        if (!Number.isFinite(timeToReach) || timeToReach <= 0) return ball.y;

        const radius = ball.radius || 0;
        const top = radius;
        const bottom = Math.max(top, canvasHeight - radius);

        let predictedY = ball.y + (ball.dy * timeToReach);

        // Odbicia od ścian: odbijaj w przedziale [top, bottom]
        let guard = 0;
        while (predictedY < top || predictedY > bottom) {
            if (predictedY < top) {
                predictedY = top + (top - predictedY);
            } else if (predictedY > bottom) {
                predictedY = bottom - (predictedY - bottom);
            }
            guard++;
            if (guard > 20) break;
        }

        // Mieszaj predykcję z bieżącą pozycją (żeby łatwiejsze poziomy nie były "laserem")
        const mixed = ball.y * (1 - strength) + predictedY * strength;
        return this.clamp(mixed, top, bottom);
    }

    clamp(value, min, max) {
        return Math.min(max, Math.max(min, value));
    }
    
    /**
     * Ustawia niestandardową konfigurację dla poziomu trudności
     * @param {String} difficulty - Nazwa poziomu trudności
     * @param {Object} config - Konfiguracja
     */
    setCustomDifficulty(difficulty, config) {
        this.difficulties[difficulty] = {
            ...this.difficulties.easy,
            ...config
        };
    }
    
    /**
     * Pobiera konfigurację dla poziomu trudności
     * @param {String} difficulty - Nazwa poziomu trudności
     * @returns {Object} Konfiguracja
     */
    getDifficultyConfig(difficulty) {
        return this.difficulties[difficulty] || this.difficulties.easy;
    }
}

// Eksportuj klasę
if (typeof window !== 'undefined') {
    window.BotAI = BotAI;
    // Utwórz globalną instancję
    window.botAI = new BotAI();
    // Freeze object to prevent modifications
    Object.freeze(window.botAI.difficulties);
}

})(); // End of IIFE
