/**
 * Gère la collecte, la persistance (via {@link SharedPreferences}) et l'accès
 * aux statistiques du jeu 2048.
 * Inclut des statistiques générales, solo, et des placeholders pour le multijoueur.
 * Charge et sauvegarde automatiquement les statistiques via SharedPreferences.
 */
package legion.muyue.best2048;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.concurrent.TimeUnit;

public class GameStats {

    // --- Constantes pour SharedPreferences ---

    /** Nom du fichier de préférences partagées utilisé pour stocker les statistiques et l'état du jeu. */
    private static final String PREFS_NAME = "Best2048_Prefs";
    /**
     * Clé pour le meilleur score global.
     * NOTE : Cette clé est également utilisée directement par MainActivity/Game pour la sauvegarde/chargement
     * de l'état du jeu. La synchronisation est gérée entre les classes.
     */
    private static final String HIGH_SCORE_KEY = "high_score";

    // Clés spécifiques aux statistiques persistantes
    private static final String STATS_TOTAL_GAMES_PLAYED = "totalGamesPlayed";
    private static final String STATS_TOTAL_GAMES_STARTED = "totalGamesStarted";
    private static final String STATS_TOTAL_MOVES = "totalMoves";
    private static final String STATS_TOTAL_PLAY_TIME_MS = "totalPlayTimeMs";
    private static final String STATS_TOTAL_MERGES = "totalMerges";
    private static final String STATS_HIGHEST_TILE = "highestTile";
    private static final String STATS_OBJECTIVE_REACHED_COUNT = "numberOfTimesObjectiveReached";
    // private static final String STATS_PERFECT_GAMES = "perfectGames"; // Clé supprimée car concept non défini
    private static final String STATS_BEST_WINNING_TIME_MS = "bestWinningTimeMs";
    private static final String STATS_WORST_WINNING_TIME_MS = "worstWinningTimeMs";

    // Clés pour les statistiques multijoueur (fonctionnalité future)
    private static final String STATS_MP_GAMES_WON = "multiplayerGamesWon";
    private static final String STATS_MP_GAMES_PLAYED = "multiplayerGamesPlayed";
    private static final String STATS_MP_BEST_WINNING_STREAK = "multiplayerBestWinningStreak";
    private static final String STATS_MP_TOTAL_SCORE = "multiplayerTotalScore";
    private static final String STATS_MP_TOTAL_TIME_MS = "multiplayerTotalTimeMs";
    private static final String STATS_MP_LOSSES = "totalMultiplayerLosses";
    private static final String STATS_MP_HIGH_SCORE = "multiplayerHighScore";


    // --- Champs de Statistiques ---

    // Générales & Solo
    /** Nombre total de parties terminées (victoire ou défaite). */
    private int totalGamesPlayed;
    /** Nombre total de parties démarrées (via bouton "Nouvelle Partie"). */
    private int totalGamesStarted;
    /** Nombre total de mouvements (swipes valides) effectués dans toutes les parties. */
    private int totalMoves;
    /** Temps total passé à jouer (en millisecondes) sur toutes les parties. */
    private long totalPlayTimeMs;
    /** Nombre total de fusions de tuiles effectuées dans toutes les parties. */
    private int totalMerges;
    /** Valeur de la plus haute tuile atteinte globalement dans toutes les parties. */
    private int highestTile;
    /** Nombre de fois où l'objectif (atteindre 2048 ou plus) a été atteint. */
    private int numberOfTimesObjectiveReached;
    // private int perfectGames; // Champ supprimé car concept non défini
    /** Meilleur temps (le plus court, en ms) pour atteindre l'objectif (>= 2048). */
    private long bestWinningTimeMs;
    /** Pire temps (le plus long, en ms) pour atteindre l'objectif (>= 2048). */
    private long worstWinningTimeMs;
    /**
     * Meilleur score global atteint. Persisté via HIGH_SCORE_KEY.
     * Synchronisé avec MainActivity/Game lors du chargement/sauvegarde.
     */
    private int overallHighScore;

    // Partie en cours (non persistées telles quelles, utilisées pour calculs en temps réel)
    /** Nombre de mouvements effectués dans la partie actuelle. */
    private int currentMoves;
    /** Timestamp (en ms) du début de la partie actuelle. Réinitialisé à chaque nouvelle partie ou reprise. */
    private long currentGameStartTimeMs;
    /** Nombre de fusions effectuées dans la partie actuelle. */
    private int mergesThisGame;

    // Multijoueur (placeholders pour fonctionnalité future)
    /** Nombre de parties multijoueur gagnées. */
    private int multiplayerGamesWon;
    /** Nombre de parties multijoueur jouées (terminées). */
    private int multiplayerGamesPlayed;
    /** Plus longue série de victoires consécutives en multijoueur. */
    private int multiplayerBestWinningStreak;
    /** Score total accumulé dans toutes les parties multijoueur. */
    private long multiplayerTotalScore;
    /** Temps total passé dans les parties multijoueur (en ms). */
    private long multiplayerTotalTimeMs;
    /** Nombre total de défaites en multijoueur. */
    private int totalMultiplayerLosses;
    /** Meilleur score atteint dans une seule partie multijoueur. */
    private int multiplayerHighestScore;

    /** Contexte applicatif nécessaire pour accéder aux SharedPreferences. */
    private final Context context;

    /**
     * Constructeur de GameStats.
     * Initialise l'objet et charge immédiatement les statistiques persistantes
     * depuis les SharedPreferences via {@link #loadStats()}.
     *
     * @param context Contexte de l'application (Activity ou Application). Utilise `getApplicationContext()` pour éviter les fuites de mémoire.
     */
    public GameStats(Context context) {
        this.context = context.getApplicationContext(); // Important: utiliser le contexte applicatif
        loadStats(); // Charge les statistiques dès l'initialisation
    }

    // --- Persistance (SharedPreferences) ---

    /**
     * Charge toutes les statistiques persistantes depuis le fichier SharedPreferences défini par {@link #PREFS_NAME}.
     * Si une clé n'existe pas, une valeur par défaut est utilisée (0, Long.MAX_VALUE pour best time, etc.).
     * Appelé automatiquement par le constructeur.
     */
    public void loadStats() {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);

        // Charge le high score (clé partagée)
        overallHighScore = prefs.getInt(HIGH_SCORE_KEY, 0);

        // Charge les statistiques générales et solo
        totalGamesPlayed = prefs.getInt(STATS_TOTAL_GAMES_PLAYED, 0);
        totalGamesStarted = prefs.getInt(STATS_TOTAL_GAMES_STARTED, 0);
        totalMoves = prefs.getInt(STATS_TOTAL_MOVES, 0);
        totalPlayTimeMs = prefs.getLong(STATS_TOTAL_PLAY_TIME_MS, 0L);
        totalMerges = prefs.getInt(STATS_TOTAL_MERGES, 0);
        highestTile = prefs.getInt(STATS_HIGHEST_TILE, 0);
        numberOfTimesObjectiveReached = prefs.getInt(STATS_OBJECTIVE_REACHED_COUNT, 0);
        // perfectGames = prefs.getInt(STATS_PERFECT_GAMES, 0); // Supprimé
        bestWinningTimeMs = prefs.getLong(STATS_BEST_WINNING_TIME_MS, Long.MAX_VALUE); // MAX_VALUE comme indicateur "pas encore de temps enregistré"
        worstWinningTimeMs = prefs.getLong(STATS_WORST_WINNING_TIME_MS, 0L);

        // Charge les statistiques multijoueur (placeholders)
        multiplayerGamesWon = prefs.getInt(STATS_MP_GAMES_WON, 0);
        multiplayerGamesPlayed = prefs.getInt(STATS_MP_GAMES_PLAYED, 0);
        multiplayerBestWinningStreak = prefs.getInt(STATS_MP_BEST_WINNING_STREAK, 0);
        multiplayerTotalScore = prefs.getLong(STATS_MP_TOTAL_SCORE, 0L);
        multiplayerTotalTimeMs = prefs.getLong(STATS_MP_TOTAL_TIME_MS, 0L);
        totalMultiplayerLosses = prefs.getInt(STATS_MP_LOSSES, 0);
        multiplayerHighestScore = prefs.getInt(STATS_MP_HIGH_SCORE, 0);
    }

    /**
     * Sauvegarde toutes les statistiques persistantes actuelles dans le fichier SharedPreferences.
     * Utilise `apply()` pour une sauvegarde asynchrone et non bloquante.
     * Devrait être appelée lorsque l'application se met en pause ou est détruite,
     * ou après une mise à jour significative des statistiques (ex: reset).
     */
    public void saveStats() {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();

        // Sauvegarde le high score (clé partagée)
        editor.putInt(HIGH_SCORE_KEY, overallHighScore);

        // Sauvegarde les statistiques générales et solo
        editor.putInt(STATS_TOTAL_GAMES_PLAYED, totalGamesPlayed);
        editor.putInt(STATS_TOTAL_GAMES_STARTED, totalGamesStarted);
        editor.putInt(STATS_TOTAL_MOVES, totalMoves);
        editor.putLong(STATS_TOTAL_PLAY_TIME_MS, totalPlayTimeMs);
        editor.putInt(STATS_TOTAL_MERGES, totalMerges);
        editor.putInt(STATS_HIGHEST_TILE, highestTile);
        editor.putInt(STATS_OBJECTIVE_REACHED_COUNT, numberOfTimesObjectiveReached);
        // editor.putInt(STATS_PERFECT_GAMES, perfectGames); // Supprimé
        editor.putLong(STATS_BEST_WINNING_TIME_MS, bestWinningTimeMs);
        editor.putLong(STATS_WORST_WINNING_TIME_MS, worstWinningTimeMs);

        // Sauvegarde les statistiques multijoueur (placeholders)
        editor.putInt(STATS_MP_GAMES_WON, multiplayerGamesWon);
        editor.putInt(STATS_MP_GAMES_PLAYED, multiplayerGamesPlayed);
        editor.putInt(STATS_MP_BEST_WINNING_STREAK, multiplayerBestWinningStreak);
        editor.putLong(STATS_MP_TOTAL_SCORE, multiplayerTotalScore);
        editor.putLong(STATS_MP_TOTAL_TIME_MS, multiplayerTotalTimeMs);
        editor.putInt(STATS_MP_LOSSES, totalMultiplayerLosses);
        editor.putInt(STATS_MP_HIGH_SCORE, multiplayerHighestScore);

        // Applique les changements de manière asynchrone
        editor.apply();
    }

    // --- Méthodes de Mise à Jour des Statistiques ---

    /**
     * Doit être appelée au début de chaque nouvelle partie (ex: clic sur "Nouvelle Partie").
     * Incrémente le compteur de parties démarrées (`totalGamesStarted`) et réinitialise
     * les statistiques spécifiques à la partie en cours (`currentMoves`, `mergesThisGame`, `currentGameStartTimeMs`).
     */
    public void startGame() {
        totalGamesStarted++;
        currentMoves = 0;
        mergesThisGame = 0;
        currentGameStartTimeMs = System.currentTimeMillis(); // Enregistre le temps de début
    }

    /**
     * Enregistre un mouvement réussi (un swipe qui a modifié l'état du plateau).
     * Incrémente le compteur de mouvements pour la partie en cours (`currentMoves`)
     * et le compteur total de mouvements (`totalMoves`).
     */
    public void recordMove() {
        currentMoves++;
        totalMoves++;
    }

    /**
     * Enregistre une ou plusieurs fusions survenues lors d'un mouvement.
     * Incrémente le compteur de fusions pour la partie en cours (`mergesThisGame`)
     * et le compteur total de fusions (`totalMerges`).
     *
     * @param numberOfMerges Le nombre de fusions distinctes réalisées lors du dernier mouvement (typiquement 1 ou plus).
     */
    public void recordMerge(int numberOfMerges) {
        if (numberOfMerges > 0) {
            mergesThisGame += numberOfMerges;
            totalMerges += numberOfMerges;
        }
    }

    /**
     * Met à jour la statistique de la plus haute tuile atteinte globalement (`highestTile`),
     * si la valeur fournie est supérieure à la valeur actuelle enregistrée.
     * Devrait être appelée après chaque mouvement qui pourrait créer une nouvelle tuile max.
     *
     * @param currentHighestTileValue La valeur de la tuile la plus haute sur le plateau à l'instant T.
     */
    public void updateHighestTile(int currentHighestTileValue) {
        if (currentHighestTileValue > this.highestTile) {
            this.highestTile = currentHighestTileValue;
            // La sauvegarde se fera via saveStats() globalement
        }
    }

    /**
     * Enregistre une victoire (atteinte de l'objectif >= 2048).
     * Incrémente le compteur de victoires (`numberOfTimesObjectiveReached`) et met à jour
     * les statistiques de meilleur/pire temps de victoire si nécessaire.
     * Appelle également {@link #endGame(long)} pour finaliser les statistiques générales de la partie.
     *
     * @param timeTakenMs Temps écoulé (en millisecondes) pour terminer cette partie gagnante.
     */
    public void recordWin(long timeTakenMs) {
        numberOfTimesObjectiveReached++;
        if (timeTakenMs < bestWinningTimeMs) {
            bestWinningTimeMs = timeTakenMs;
        }
        // Utiliser >= pour le pire temps permet de l'enregistrer même si c'est le premier
        if (timeTakenMs >= worstWinningTimeMs) {
            worstWinningTimeMs = timeTakenMs;
        }
        endGame(timeTakenMs); // Finalise aussi le temps total et le compteur de parties jouées
    }

    /**
     * Enregistre une défaite (Game Over - plateau plein sans mouvement possible).
     * Calcule le temps écoulé pour la partie et appelle {@link #endGame(long)}
     * pour finaliser les statistiques générales.
     */
    public void recordLoss() {
        // Calcule le temps écoulé pour cette partie avant de la finaliser
        long timeTakenMs = System.currentTimeMillis() - currentGameStartTimeMs;
        endGame(timeTakenMs);
    }

    /**
     * Finalise les statistiques générales à la fin d'une partie (victoire ou défaite).
     * Incrémente le compteur de parties jouées (`totalGamesPlayed`) et ajoute le temps
     * de la partie terminée au temps de jeu total (`totalPlayTimeMs`).
     *
     * @param timeTakenMs Temps total (en millisecondes) de la partie qui vient de se terminer.
     */
    private void endGame(long timeTakenMs) {
        totalGamesPlayed++;
        addPlayTime(timeTakenMs); // Ajoute la durée de la partie terminée au total
        // Ne pas réinitialiser currentGameStartTimeMs ici, car une nouvelle partie
        // pourrait ne pas commencer immédiatement (ex: affichage dialogue Game Over).
        // startGame() s'en chargera.
    }

    /**
     * Ajoute une durée (en millisecondes) au temps de jeu total enregistré (`totalPlayTimeMs`).
     * Typiquement appelé dans `onPause` de l'activité pour enregistrer le temps écoulé
     * depuis `onResume` ou `startGame`.
     *
     * @param durationMs Durée positive (en millisecondes) à ajouter au temps total.
     */
    public void addPlayTime(long durationMs) {
        if (durationMs > 0) {
            this.totalPlayTimeMs += durationMs;
        }
    }

    /**
     * Réinitialise toutes les statistiques (générales, solo, multijoueur, y compris le high score global)
     * à leurs valeurs par défaut (0 ou équivalent).
     * Après la réinitialisation, les nouvelles valeurs sont immédiatement sauvegardées
     * dans les SharedPreferences via {@link #saveStats()}.
     */
    public void resetStats() {
        // Réinitialise toutes les variables membres à 0 ou valeur initiale
        overallHighScore = 0;
        totalGamesPlayed = 0;
        totalGamesStarted = 0;
        totalMoves = 0;
        totalPlayTimeMs = 0L;
        totalMerges = 0;
        highestTile = 0;
        numberOfTimesObjectiveReached = 0;
        // perfectGames = 0; // Supprimé
        bestWinningTimeMs = Long.MAX_VALUE;
        worstWinningTimeMs = 0L;

        // Réinitialise les stats multijoueur (placeholders)
        multiplayerGamesWon = 0;
        multiplayerGamesPlayed = 0;
        multiplayerBestWinningStreak = 0;
        multiplayerTotalScore = 0L;
        multiplayerTotalTimeMs = 0L;
        totalMultiplayerLosses = 0;
        multiplayerHighestScore = 0;

        // Réinitialise les stats de la partie en cours (au cas où)
        currentMoves = 0;
        mergesThisGame = 0;
        currentGameStartTimeMs = 0L; // Ou System.currentTimeMillis() si on considère que reset démarre une nouvelle session? Non, 0 est plus sûr.

        // Sauvegarde immédiatement les statistiques réinitialisées
        saveStats();
    }

    // --- Getters pour l'affichage ---

    /** @return Le nombre total de parties terminées (victoire ou défaite). */
    public int getTotalGamesPlayed() { return totalGamesPlayed; }
    /** @return Le nombre total de parties démarrées. */
    public int getTotalGamesStarted() { return totalGamesStarted; }
    /** @return Le nombre total de mouvements valides effectués. */
    public int getTotalMoves() { return totalMoves; }
    /** @return Le nombre de mouvements dans la partie en cours. */
    public int getCurrentMoves() { return currentMoves; }
    /** @return Le temps total de jeu accumulé (en ms). */
    public long getTotalPlayTimeMs() { return totalPlayTimeMs; }
    /** @return Le timestamp (en ms) du début de la partie en cours. Utile pour calculer la durée en temps réel. */
    public long getCurrentGameStartTimeMs() { return currentGameStartTimeMs; }
    /** @return Le nombre de fusions dans la partie en cours. */
    public int getMergesThisGame() { return mergesThisGame; }
    /** @return Le nombre total de fusions effectuées. */
    public int getTotalMerges() { return totalMerges; }
    /** @return La valeur de la plus haute tuile atteinte globalement. */
    public int getHighestTile() { return highestTile; }
    /** @return Le nombre de fois où l'objectif (>= 2048) a été atteint. */
    public int getNumberOfTimesObjectiveReached() { return numberOfTimesObjectiveReached; }
    // public int getPerfectGames() { return perfectGames; } // Supprimé
    /** @return Le meilleur temps (le plus court, en ms) pour gagner une partie, ou Long.MAX_VALUE si aucune victoire. */
    public long getBestWinningTimeMs() { return bestWinningTimeMs; }
    /** @return Le pire temps (le plus long, en ms) pour gagner une partie, ou 0 si aucune victoire. */
    public long getWorstWinningTimeMs() { return worstWinningTimeMs; }
    /** @return Le meilleur score global enregistré. */
    public int getOverallHighScore() { return overallHighScore; }

    // Getters Multiplayer (fonctionnalité future)
    /** @return Nombre de parties multijoueur gagnées. */
    public int getMultiplayerGamesWon() { return multiplayerGamesWon; }
    /** @return Nombre de parties multijoueur jouées. */
    public int getMultiplayerGamesPlayed() { return multiplayerGamesPlayed; }
    /** @return Meilleure série de victoires consécutives en multijoueur. */
    public int getMultiplayerBestWinningStreak() { return multiplayerBestWinningStreak; }
    /** @return Score total accumulé en multijoueur. */
    public long getMultiplayerTotalScore() { return multiplayerTotalScore; }
    /** @return Temps total passé en multijoueur (en ms). */
    public long getMultiplayerTotalTimeMs() { return multiplayerTotalTimeMs; }
    /** @return Nombre total de défaites en multijoueur. */
    public int getTotalMultiplayerLosses() { return totalMultiplayerLosses; }
    /** @return Meilleur score atteint dans une partie multijoueur. */
    public int getMultiplayerHighestScore() { return multiplayerHighestScore; }

    // --- Setters ---

    /**
     * Met à jour la valeur interne du meilleur score global (`overallHighScore`).
     * La mise à jour n'a lieu que si le score fourni est strictement supérieur
     * au meilleur score actuel. La sauvegarde effective dans SharedPreferences
     * se fait via un appel ultérieur à {@link #saveStats()}.
     *
     * @param highScore Le nouveau score à potentiellement définir comme meilleur score.
     */
    public void setHighestScore(int highScore) {
        // Met à jour seulement si la nouvelle valeur est meilleure
        if (highScore > this.overallHighScore) {
            this.overallHighScore = highScore;
            // La sauvegarde se fait via saveStats() globalement, pas ici directement.
        }
    }

    /**
     * Définit le timestamp (en ms) du début de la partie en cours.
     * Typiquement utilisé par MainActivity dans `onResume` pour reprendre le chronomètre.
     *
     * @param timeMs Le timestamp en millisecondes.
     */
    public void setCurrentGameStartTimeMs(long timeMs) {
        this.currentGameStartTimeMs = timeMs;
    }

    // --- Méthodes Calculées ---

    /**
     * Calcule le temps moyen passé par partie terminée (solo).
     *
     * @return Le temps moyen par partie en millisecondes, ou 0 si aucune partie n'a été terminée.
     */
    public long getAverageGameTimeMs() {
        return (totalGamesPlayed > 0) ? totalPlayTimeMs / totalGamesPlayed : 0L;
    }

    /**
     * Calcule le score moyen par partie multijoueur terminée.
     * (Basé sur les statistiques multijoueur placeholder).
     *
     * @return Le score moyen entier par partie multijoueur, ou 0 si aucune partie multijoueur n'a été jouée.
     */
    public int getMultiplayerAverageScore() {
        return (multiplayerGamesPlayed > 0) ? (int)(multiplayerTotalScore / multiplayerGamesPlayed) : 0;
    }

    /**
     * Calcule le temps moyen passé par partie multijoueur terminée.
     * (Basé sur les statistiques multijoueur placeholder).
     *
     * @return Le temps moyen par partie multijoueur en millisecondes, ou 0 si aucune partie multijoueur n'a été jouée.
     */
    public long getMultiplayerAverageTimeMs() {
        return (multiplayerGamesPlayed > 0) ? multiplayerTotalTimeMs / multiplayerGamesPlayed : 0L;
    }

    /**
     * Formate une durée donnée en millisecondes en une chaîne de caractères lisible.
     * Le format est "hh:mm:ss" si la durée est d'une heure ou plus, sinon "mm:ss".
     *
     * @param milliseconds Durée totale en millisecondes.
     * @return Une chaîne formatée représentant la durée.
     */
    @SuppressLint("DefaultLocale") // Justifié car le format hh:mm:ss est standard et non dépendant de la locale
    public static String formatTime(long milliseconds) {
        if (milliseconds < 0) { milliseconds = 0; } // Gère les cas négatifs

        long hours = TimeUnit.MILLISECONDS.toHours(milliseconds);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60; // Minutes restantes après les heures
        long seconds = TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60; // Secondes restantes après les minutes

        if (hours > 0) {
            // Format avec heures si nécessaire
            return String.format("%02d:%02d:%02d", hours, minutes, seconds);
        } else {
            // Format minutes:secondes sinon
            return String.format("%02d:%02d", minutes, seconds);
        }
    }
}