import {
    GYRO_DEADZONE_ANDROID,
    GYRO_MAX_TILT_ANDROID,
    GYRO_SENSITIVITY_ANDROID,
    GYRO_CALIBRATION_SAMPLES_ANDROID,
    GYRO_INVERT_BETA_ANDROID,
    GYRO_USE_GAMMA_ANDROID,
    GYRO_USE_ALPHA_ANDROID,
    GYRO_DEADZONE_IOS,
    GYRO_MAX_TILT_IOS,
    GYRO_SENSITIVITY_IOS,
    GYRO_CALIBRATION_SAMPLES_IOS,
    GYRO_INVERT_BETA_IOS,
    GYRO_USE_GAMMA_IOS,
    GYRO_USE_ALPHA_IOS,
} from '../utils/constants';

/**
 * Gestion du gyroscope pour iOS et Android
 * Retourne une valeur normalisée entre -1 et 1
 *
 * Configuration centralisée dans constants.ts pour faciliter le débogage:
 * - iOS: GYRO_*_IOS (sensibilité réduite, pas d'inversion beta)
 * - Android: GYRO_*_ANDROID (sensibilité élevée, inversion beta)
 *
 * Pour ajuster les contrôles, modifier les constantes dans utils/constants.ts
 */
export class GyroControl {
    private tiltValue: number = 0;
    private isActive: boolean = false;
    private lastEventLogTime: number = 0;
    private lastTiltLogTime: number = 0;
    private lastAlpha: number = 0;  // Rotation boussole (0-360°)
    private lastBeta: number = 0;   // Inclinaison avant/arrière
    private lastGamma: number = 0;  // Inclinaison gauche/droite
    private lastAngle: number = 0;
    private lastAbs: boolean | null = null;
    private lastEventAt: number = 0;
    private baselineTilt: number = 0; // Baseline pour calibration
    private isCalibrated: boolean = false;
    private calibrationSamples: number[] = [];
    private readonly calibrationSamplesCount: number; // Nombre d'échantillons pour moyenne (varie selon plateforme)

    // Configuration spécifique selon la plateforme
    private readonly isIOS: boolean;
    private readonly deadzone: number;
    private readonly maxTilt: number;
    private readonly sensitivity: number;
    private readonly invertBeta: boolean; // Inverser la lecture du beta?
    private readonly useGamma: boolean;   // Utiliser gamma au lieu de beta en paysage?
    private readonly useAlpha: boolean;   // Utiliser alpha (rotation boussole)?

    constructor() {
        // Détecter iOS
        this.isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
                     (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);

        // Configurer selon la plateforme (toutes les valeurs viennent de constants.ts)
        if (this.isIOS) {
            this.deadzone = GYRO_DEADZONE_IOS;
            this.maxTilt = GYRO_MAX_TILT_IOS;
            this.sensitivity = GYRO_SENSITIVITY_IOS;
            this.calibrationSamplesCount = GYRO_CALIBRATION_SAMPLES_IOS;
            this.invertBeta = GYRO_INVERT_BETA_IOS;
            this.useGamma = GYRO_USE_GAMMA_IOS;
            this.useAlpha = GYRO_USE_ALPHA_IOS;
            console.log('[GyroControl] ✅ Configuration iOS chargée depuis constants.ts', {
                deadzone: this.deadzone,
                maxTilt: this.maxTilt,
                sensitivity: this.sensitivity,
                calibrationSamples: this.calibrationSamplesCount,
                invertBeta: this.invertBeta,
                useGamma: this.useGamma,
                useAlpha: this.useAlpha
            });
        } else {
            this.deadzone = GYRO_DEADZONE_ANDROID;
            this.maxTilt = GYRO_MAX_TILT_ANDROID;
            this.sensitivity = GYRO_SENSITIVITY_ANDROID;
            this.calibrationSamplesCount = GYRO_CALIBRATION_SAMPLES_ANDROID;
            this.invertBeta = GYRO_INVERT_BETA_ANDROID;
            this.useGamma = GYRO_USE_GAMMA_ANDROID;
            this.useAlpha = GYRO_USE_ALPHA_ANDROID;
            console.log('[GyroControl] ✅ Configuration Android chargée depuis constants.ts', {
                deadzone: this.deadzone,
                maxTilt: this.maxTilt,
                sensitivity: this.sensitivity,
                calibrationSamples: this.calibrationSamplesCount,
                invertBeta: this.invertBeta,
                useGamma: this.useGamma,
                useAlpha: this.useAlpha
            });
        }

        this.setupGyroscope();
    }

    /**
     * Configure les listeners du gyroscope
     * Note: La permission doit avoir été demandée AVANT (via MenuScene sur iOS)
     */
    private setupGyroscope(): void {
        if (!window.DeviceOrientationEvent) {
            console.warn('Gyroscope non disponible sur cet appareil');
            return;
        }

        // Activer directement le gyroscope
        // La permission a déjà été demandée dans MenuScene pour iOS
        this.enableGyroscope();
    }

    /**
     * Active le listener du gyroscope
     */
    private enableGyroscope(): void {
        window.addEventListener('deviceorientation', (event) => {
            this.handleOrientation(event);
        });
        this.isActive = true;
        console.log('✅ Gyroscope activé');
    }

    /**
     * Gère les événements d'orientation
     */
    private handleOrientation(event: DeviceOrientationEvent): void {
        if (!this.isActive) return;

        // Déterminer l'axe horizontal en fonction de l'orientation écran
        const angle = (window.screen.orientation?.angle ?? (window as any).orientation ?? 0) as number;
        const alpha = event.alpha ?? 0; // rotation boussole (0-360°)
        const beta = event.beta ?? 0;  // inclinaison avant/arrière
        const gamma = event.gamma ?? 0; // inclinaison gauche/droite
        const now = Date.now();
        this.lastAlpha = alpha;
        this.lastBeta = beta;
        this.lastGamma = gamma;
        this.lastAngle = angle;
        this.lastAbs = event.absolute ?? null;
        this.lastEventAt = now;
        if (now - this.lastEventLogTime > 1000) {
            console.log('[Gyro] event', {
                angle,
                alpha,
                beta,
                gamma,
                abs: event.absolute ?? null,
            });
            this.lastEventLogTime = now;
        }

        // IMPORTANT : Déterminer l'axe à utiliser selon l'orientation réelle
        // iOS: beta (avant/arrière), Android: peut utiliser alpha (rotation boussole), gamma, ou beta
        let horizontalTiltDeg: number;

        // Si on utilise alpha (rotation boussole - Android uniquement)
        if (this.useAlpha) {
            // Alpha va de 0 à 360°
            // Convertir en -180 à 180 pour avoir une baseline centrée
            let normalizedAlpha = alpha;
            if (normalizedAlpha > 180) {
                normalizedAlpha -= 360;
            }
            horizontalTiltDeg = normalizedAlpha;
        }
        // ANDROID : Forcer l'utilisation de BETA quelle que soit l'orientation
        else if (!this.isIOS && !this.useGamma && !this.useAlpha) {
            // Android avec configuration BETA : utiliser beta directement
            horizontalTiltDeg = this.invertBeta ? -beta : beta;

            // Ajuster selon l'orientation du téléphone
            // Si angle = 90° (paysage avec home button à droite) ou -90° (home button à gauche)
            if (Math.abs(angle - 90) < 45) {
                // Paysage normal (90°) : pas d'inversion supplémentaire
                // horizontalTiltDeg reste tel quel
            } else if (Math.abs(angle + 90) < 45 || Math.abs(angle - 270) < 45) {
                // Paysage inversé (-90° ou 270°) : inverser
                horizontalTiltDeg = -horizontalTiltDeg;
            }
        }
        // Sinon, si angle est proche de 0° ou 180° (mode paysage classique)
        else if (Math.abs(angle) < 45 || Math.abs(angle - 180) < 45 || Math.abs(angle + 180) < 45) {
            // Mode paysage
            if (this.useGamma) {
                // Android : utiliser gamma pour l'inclinaison gauche/droite
                horizontalTiltDeg = gamma;

                // Inverser si paysage inversé (angle proche de 180)
                if (Math.abs(angle - 180) < 45 || Math.abs(angle + 180) < 45) {
                    horizontalTiltDeg = -horizontalTiltDeg;
                }
            } else {
                // iOS : utiliser beta pour l'inclinaison avant/arrière
                // L'inversion du beta est configurée dans constants.ts selon la plateforme
                horizontalTiltDeg = this.invertBeta ? -beta : beta;

                // Inverser si paysage inversé (angle proche de 180)
                if (Math.abs(angle - 180) < 45 || Math.abs(angle + 180) < 45) {
                    horizontalTiltDeg = -horizontalTiltDeg;
                }
            }
        }
        // Si angle proche de 90° ou -90°/270° (mode portrait)
        else {
            // Mode portrait : utiliser gamma pour l'inclinaison gauche/droite
            horizontalTiltDeg = gamma;

            // Ajuster le signe selon l'orientation
            if (angle < 0 || angle > 180) {
                horizontalTiltDeg = -gamma;
            }
        }

        // Calibration automatique : collecter les premiers échantillons
        if (!this.isCalibrated) {
            this.calibrationSamples.push(horizontalTiltDeg);
            if (this.calibrationSamples.length >= this.calibrationSamplesCount) {
                // Calculer la moyenne pour établir la baseline
                this.baselineTilt = this.calibrationSamples.reduce((sum, val) => sum + val, 0) / this.calibrationSamples.length;
                this.isCalibrated = true;
                console.log(`[Gyro ${this.isIOS ? 'iOS' : 'Android'}] ✅ Calibration terminée - Baseline: ${this.baselineTilt.toFixed(2)}° (${this.calibrationSamplesCount} échantillons)`);
            } else {
                console.log(`[Gyro ${this.isIOS ? 'iOS' : 'Android'}] Calibration en cours... (${this.calibrationSamples.length}/${this.calibrationSamplesCount})`);
                this.tiltValue = 0; // Pas de mouvement pendant la calibration
                return;
            }
        }

        // Soustraire la baseline pour obtenir le tilt relatif à la position neutre
        const relativeTilt = horizontalTiltDeg - this.baselineTilt;

        // Appliquer la deadzone (spécifique à la plateforme)
        if (Math.abs(relativeTilt) < this.deadzone) {
            this.tiltValue = 0;
            return;
        }

        // Normaliser entre -1 et 1 (avec maxTilt spécifique à la plateforme)
        let normalizedTilt = relativeTilt / this.maxTilt;

        // Clamper entre -1 et 1
        normalizedTilt = Math.max(-1, Math.min(1, normalizedTilt));

        // Pas d'inversion finale - elle est déjà faite au niveau de la lecture du beta/gamma
        // iOS: beta direct → pencher en arrière = beta négatif → mouvement droite
        // Android: gamma direct → pencher à droite = gamma positif → mouvement droite
        this.tiltValue = normalizedTilt;

        if (now - this.lastTiltLogTime > 1000) {
            const axisUsed = this.useAlpha ? 'alpha' : (this.useGamma ? 'gamma' : 'beta');
            console.log(`[Gyro ${this.isIOS ? 'iOS' : 'Android'}] tilt normalized`, this.tiltValue,
                `(axe: ${axisUsed}, raw: ${horizontalTiltDeg.toFixed(1)}°, baseline: ${this.baselineTilt.toFixed(1)}°, relative: ${relativeTilt.toFixed(1)}°, deadzone: ${this.deadzone}°, maxTilt: ${this.maxTilt}°)`);
            this.lastTiltLogTime = now;
        }
    }

    /**
     * Retourne la valeur actuelle du tilt normalisée (-1 à 1)
     */
    public getTiltValue(): number {
        return this.tiltValue;
    }

    /**
     * Retourne la vitesse calculée depuis le tilt (avec sensibilité spécifique à la plateforme)
     */
    public getVelocity(): number {
        return this.tiltValue * this.sensitivity;
    }

    /**
     * Calibre le gyroscope (définit l'orientation actuelle comme neutre)
     */
    public calibrate(): void {
        // Reset de la calibration pour recalculer la baseline
        this.isCalibrated = false;
        this.calibrationSamples = [];
        this.baselineTilt = 0;
        this.tiltValue = 0;
        console.log('[Gyro] Recalibration demandée...');
    }

    /**
     * Active/désactive le gyroscope
     */
    public setActive(active: boolean): void {
        this.isActive = active;
        if (!active) {
            this.tiltValue = 0;
        }
    }

    /**
     * Vérifie si le gyroscope est actif
     */
    public getIsActive(): boolean {
        return this.isActive;
    }

    /**
     * Détruit le contrôleur (cleanup)
     */
    public destroy(): void {
        this.isActive = false;
        this.tiltValue = 0;
    }

    /**
     * Debug info for on-screen diagnostics.
     */
    public getDebugInfo(): {
        active: boolean;
        tilt: number;
        alpha: number;
        beta: number;
        gamma: number;
        angle: number;
        abs: boolean | null;
        lastEventAt: number;
        calibrated: boolean;
        baseline: number;
        platform: string;
        deadzone: number;
        maxTilt: number;
        sensitivity: number;
        invertBeta: boolean;
        useGamma: boolean;
        useAlpha: boolean;
    } {
        return {
            active: this.isActive,
            tilt: this.tiltValue,
            alpha: this.lastAlpha,
            beta: this.lastBeta,
            gamma: this.lastGamma,
            angle: this.lastAngle,
            abs: this.lastAbs,
            lastEventAt: this.lastEventAt,
            calibrated: this.isCalibrated,
            baseline: this.baselineTilt,
            platform: this.isIOS ? 'iOS' : 'Android',
            deadzone: this.deadzone,
            maxTilt: this.maxTilt,
            sensitivity: this.sensitivity,
            invertBeta: this.invertBeta,
            useGamma: this.useGamma,
            useAlpha: this.useAlpha,
        };
    }
}
