package legion.muyue.best2048;

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.gridlayout.widget.GridLayout;

import java.util.Objects; // Pour la comparaison d'ID

import legion.muyue.best2048.data.GameInfo;
import legion.muyue.best2048.data.GameStateResponse;
import legion.muyue.best2048.data.MoveRequest;
import legion.muyue.best2048.network.ApiClient;
import legion.muyue.best2048.network.ApiService;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MultiplayerActivity extends AppCompatActivity {

    private static final String TAG = "MultiplayerActivity";
    private static final int BOARD_SIZE = 4;
    private static final long POLLING_INTERVAL_MS = 3000; // Intervalle de polling (3 sec) - Inefficace !

    // UI
    private GridLayout boardGridLayoutMulti;
    private TextView myScoreLabelMulti;
    private TextView opponentScoreLabelMulti;
    private TextView turnIndicatorMulti;
    private TextView statusTextMulti;
    private ProgressBar loadingIndicatorMulti;
    private TextView[][] tileViewsMulti = new TextView[BOARD_SIZE][BOARD_SIZE];

    // Network
    private ApiService apiService;

    // Game State
    private String currentGameId = null;
    private String myPlayerId = "Player1_Temp"; // TODO: Remplacer par une vraie identification
    private String opponentPlayerId = null;
    private GameStateResponse currentGameState = null;

    // Polling Handler
    private Handler pollingHandler;
    private Runnable pollingRunnable;
    private boolean isPollingActive = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiplayer);

        findViewsMulti();
        apiService = ApiClient.getApiService();
        pollingHandler = new Handler(Looper.getMainLooper());

        setupSwipeListenerMulti();
        initMultiplayerGame();
    }

    @Override
    protected void onPause() {
        super.onPause();
        stopPolling(); // Arrête le polling quand l'activité n'est plus visible
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (currentGameId != null && currentGameState != null && !currentGameState.isGameOver()) {
            startPolling(); // Redémarre le polling si une partie est en cours
        }
    }

    private void findViewsMulti() {
        boardGridLayoutMulti = findViewById(R.id.gameBoardMulti);
        myScoreLabelMulti = findViewById(R.id.myScoreLabelMulti);
        opponentScoreLabelMulti = findViewById(R.id.opponentScoreLabelMulti);
        turnIndicatorMulti = findViewById(R.id.turnIndicatorMulti);
        statusTextMulti = findViewById(R.id.multiplayerStatusText);
        loadingIndicatorMulti = findViewById(R.id.loadingIndicatorMulti);
    }

    /** Tente de créer ou rejoindre une partie multijoueur. */
    private void initMultiplayerGame() {
        showLoading(true);
        statusTextMulti.setText("Recherche d'une partie...");
        // TODO: Implémenter la logique d'obtention de myPlayerId
        // Appel API pour créer/rejoindre
        apiService.createOrJoinGame(myPlayerId).enqueue(new Callback<GameInfo>() {
            @Override
            public void onResponse(@NonNull Call<GameInfo> call, @NonNull Response<GameInfo> response) {
                if (response.isSuccessful() && response.body() != null) {
                    GameInfo gameInfo = response.body();
                    currentGameId = gameInfo.getGameId();
                    // TODO: Déterminer opponentPlayerId basé sur gameInfo.getPlayer1Id/getPlayer2Id
                    opponentPlayerId = myPlayerId.equals(gameInfo.getPlayer1Id()) ? gameInfo.getPlayer2Id() : gameInfo.getPlayer1Id();

                    Log.i(TAG, "Partie rejointe/créée: ID=" + currentGameId + ", Adversaire=" + opponentPlayerId);
                    statusTextMulti.setText("Partie trouvée ! ID: " + currentGameId);
                    fetchGameState(); // Récupère l'état initial
                } else {
                    Log.e(TAG, "Erreur création/rejoindre partie: " + response.code());
                    handleNetworkError("Impossible de créer ou rejoindre une partie.");
                }
            }

            @Override
            public void onFailure(@NonNull Call<GameInfo> call, @NonNull Throwable t) {
                Log.e(TAG, "Échec création/rejoindre partie", t);
                handleNetworkError("Échec de connexion au serveur.");
            }
        });
    }

    /** Récupère l'état actuel du jeu depuis le serveur. */
    private void fetchGameState() {
        if (currentGameId == null) return;
        // Ne montre le chargement que si pas déjà en polling (pour fluidité)
        if(!isPollingActive) showLoading(true);

        apiService.getGameState(currentGameId).enqueue(new Callback<GameStateResponse>() {
            @Override
            public void onResponse(@NonNull Call<GameStateResponse> call, @NonNull Response<GameStateResponse> response) {
                showLoading(false);
                if (response.isSuccessful() && response.body() != null) {
                    currentGameState = response.body();
                    Log.d(TAG, "État du jeu récupéré. Tour de: " + currentGameState.getCurrentPlayerId());
                    updateMultiplayerUI(currentGameState); // Met à jour l'affichage
                    if (!currentGameState.isGameOver()) {
                        startPolling(); // Commence ou continue le polling
                    } else {
                        stopPolling(); // Arrête si la partie est finie
                        // Afficher message fin de partie
                    }
                } else {
                    Log.e(TAG, "Erreur récupération état partie: " + response.code());
                    // Gérer l'erreur, peut-être réessayer ?
                    statusTextMulti.setText("Erreur récupération état...");
                }
            }

            @Override
            public void onFailure(@NonNull Call<GameStateResponse> call, @NonNull Throwable t) {
                Log.e(TAG, "Échec récupération état partie", t);
                showLoading(false);
                handleNetworkError("Échec connexion pour état partie.");
                // Arrêter le polling en cas d'échec réseau ?
                // stopPolling();
            }
        });
    }

    /** Démarre le polling périodique pour récupérer l'état du jeu. */
    private void startPolling() {
        if (isPollingActive || currentGameId == null || currentGameState == null || currentGameState.isGameOver()) {
            return; // Ne pas démarrer si déjà actif, pas de partie, ou partie finie
        }
        Log.d(TAG, "Démarrage du polling...");
        isPollingActive = true;
        pollingRunnable = new Runnable() {
            @Override
            public void run() {
                if (!isPollingActive) return; // Vérifie si on doit s'arrêter
                fetchGameState(); // Récupère l'état
                // Replanifie après l'intervalle (même si fetchGameState est en cours)
                // Pour éviter accumulation, on pourrait replanifier dans onResponse/onFailure
                // Mais pour simple polling, c'est ok.
                pollingHandler.postDelayed(this, POLLING_INTERVAL_MS);
            }
        };
        pollingHandler.post(pollingRunnable); // Lance la première exécution
    }


    /** Arrête le polling périodique. */
    private void stopPolling() {
        if (isPollingActive && pollingHandler != null && pollingRunnable != null) {
            Log.d(TAG, "Arrêt du polling.");
            isPollingActive = false;
            pollingHandler.removeCallbacks(pollingRunnable);
        }
    }

    /** Met à jour l'interface multijoueur basée sur l'état reçu. */
    private void updateMultiplayerUI(GameStateResponse state) {
        if (state == null) return;

        // Met à jour les scores
        // TODO: Adapter la logique getMyScore/getOpponentScore avec les vrais ID
        myScoreLabelMulti.setText("Moi:\n" + state.getMyScore(myPlayerId));
        opponentScoreLabelMulti.setText("Autre:\n" + state.getOpponentScore(myPlayerId));

        // Met à jour l'indicateur de tour
        boolean myTurn = myPlayerId.equals(state.getCurrentPlayerId());
        turnIndicatorMulti.setText(myTurn ? "Votre Tour" : "Tour Adversaire");
        turnIndicatorMulti.setTextColor(ContextCompat.getColor(this, myTurn ? R.color.tile_16 : R.color.text_tile_low)); // Exemple couleur

        // Met à jour le plateau
        syncBoardViewMulti(state.getBoard());

        // Met à jour le statut
        if(state.isGameOver()){
            statusTextMulti.setText(state.getWinnerId() != null ? (state.getWinnerId().equals(myPlayerId) ? "Vous avez gagné !" : "Vous avez perdu.") : "Partie Terminée !");
        } else if (!myTurn) {
            statusTextMulti.setText("En attente du coup adverse...");
        } else {
            statusTextMulti.setText(""); // Vide si c'est notre tour et pas fini
        }
    }

    /** Synchronise la vue de la grille multijoueur avec un état de plateau donné. */
    private void syncBoardViewMulti(int[][] boardState) {
        if (boardState == null || boardState.length != BOARD_SIZE || boardState[0].length != BOARD_SIZE) {
            Log.e(TAG, "syncBoardViewMulti: État du plateau invalide.");
            return;
        }
        // Logique similaire à syncBoardView de MainActivity
        boardGridLayoutMulti.removeAllViews(); // Simplification par reset complet
        tileViewsMulti = new TextView[BOARD_SIZE][BOARD_SIZE];

        for (int r = 0; r < BOARD_SIZE; r++) {
            for (int c = 0; c < BOARD_SIZE; c++) {
                // Ajout fond (peut être optimisé si le fond ne change jamais)
                View backgroundCell = new View(this);
                backgroundCell.setBackgroundResource(R.drawable.tile_background);
                backgroundCell.getBackground().setTintList(ContextCompat.getColorStateList(this, R.color.tile_empty));
                GridLayout.LayoutParams bgParams = new GridLayout.LayoutParams(GridLayout.spec(r, 1f), GridLayout.spec(c, 1f));
                bgParams.width = 0; bgParams.height = 0;
                int margin = (int) getResources().getDimension(R.dimen.tile_margin);
                bgParams.setMargins(margin, margin, margin, margin);
                backgroundCell.setLayoutParams(bgParams);
                boardGridLayoutMulti.addView(backgroundCell);

                // Ajout tuile si valeur > 0
                int value = boardState[r][c];
                if (value > 0) {
                    TextView tileView = createTileTextViewMulti(value, r, c); // Utilise helper adapté
                    tileViewsMulti[r][c] = tileView;
                    boardGridLayoutMulti.addView(tileView);
                } else {
                    tileViewsMulti[r][c] = null;
                }
            }
        }
    }

    /** Crée une TextView pour une tuile multijoueur (similaire à MainActivity). */
    private TextView createTileTextViewMulti(int value, int row, int col) {
        TextView tileTextView = new TextView(this);
        setTileStyleMulti(tileTextView, value); // Utilise helper de style adapté

        GridLayout.LayoutParams params = new GridLayout.LayoutParams(GridLayout.spec(row, 1f), GridLayout.spec(col, 1f));
        params.width = 0; params.height = 0;
        int margin = (int) getResources().getDimension(R.dimen.tile_margin);
        params.setMargins(margin, margin, margin, margin);
        tileTextView.setLayoutParams(params);
        return tileTextView;
    }

    /** Applique le style à une tuile (similaire à MainActivity). */
    private void setTileStyleMulti(TextView tileTextView, int value) {
        // Copier/Adapter la logique de setTileStyle de MainActivity ici
        tileTextView.setText(value > 0 ? String.valueOf(value) : "");
        tileTextView.setGravity(Gravity.CENTER);
        tileTextView.setTypeface(null, android.graphics.Typeface.BOLD);
        int backgroundColorId; int textColorId; int textSizeId;
        switch (value) {
            case 0:    backgroundColorId = R.color.tile_empty; textColorId = android.R.color.transparent; textSizeId = R.dimen.text_size_tile_small; break;
            case 2:    backgroundColorId = R.color.tile_2;     textColorId = R.color.text_tile_low;     textSizeId = R.dimen.text_size_tile_small; break;
            case 4:    backgroundColorId = R.color.tile_4;     textColorId = R.color.text_tile_low;     textSizeId = R.dimen.text_size_tile_small; break;
            // ... autres cas ...
            default:   backgroundColorId = R.color.tile_super;   textColorId = R.color.text_tile_high;    textSizeId = R.dimen.text_size_tile_large; break;
        }
        tileTextView.setBackgroundResource(R.drawable.tile_background);
        tileTextView.getBackground().setTint(ContextCompat.getColor(this, backgroundColorId));
        tileTextView.setTextColor(ContextCompat.getColor(this, textColorId));
        tileTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(textSizeId));
    }


    /** Configure le listener de swipe pour le plateau multijoueur. */
    @SuppressLint("ClickableViewAccessibility")
    private void setupSwipeListenerMulti() {
        boardGridLayoutMulti.setOnTouchListener(new OnSwipeTouchListener(this, new OnSwipeTouchListener.SwipeListener() {
            @Override public void onSwipeTop() { handleMultiplayerSwipe(Direction.UP); }
            @Override public void onSwipeBottom() { handleMultiplayerSwipe(Direction.DOWN); }
            @Override public void onSwipeLeft() { handleMultiplayerSwipe(Direction.LEFT); }
            @Override public void onSwipeRight() { handleMultiplayerSwipe(Direction.RIGHT); }
        }));
    }

    /** Gère un swipe dans le contexte multijoueur. */
    private void handleMultiplayerSwipe(Direction direction) {
        if (currentGameState == null || currentGameId == null || currentGameState.isGameOver()) {
            Log.d(TAG, "Swipe ignoré (jeu non prêt ou terminé).");
            return;
        }
        // Vérifie si c'est notre tour
        if (!myPlayerId.equals(currentGameState.getCurrentPlayerId())) {
            Log.d(TAG, "Swipe ignoré (pas notre tour).");
            Toast.makeText(this, "Ce n'est pas votre tour.", Toast.LENGTH_SHORT).show();
            return;
        }

        Log.d(TAG, "Swipe détecté: " + direction + ". Envoi du mouvement...");
        showLoading(true); // Affiche indicateur pendant l'envoi
        statusTextMulti.setText("Envoi du mouvement...");
        stopPolling(); // Arrête le polling pendant qu'on joue notre coup

        String directionString = direction.name(); // "UP", "DOWN", etc.
        MoveRequest moveRequest = new MoveRequest(directionString, myPlayerId);

        apiService.makeMove(currentGameId, moveRequest).enqueue(new Callback<GameStateResponse>() {
            @Override
            public void onResponse(@NonNull Call<GameStateResponse> call, @NonNull Response<GameStateResponse> response) {
                showLoading(false);
                if (response.isSuccessful() && response.body() != null) {
                    Log.i(TAG, "Mouvement envoyé avec succès.");
                    currentGameState = response.body();
                    updateMultiplayerUI(currentGameState); // Met à jour l'UI avec le nouvel état
                    if (!currentGameState.isGameOver()) {
                        startPolling(); // Redémarre le polling pour attendre l'adversaire
                    } else {
                        // Afficher message fin de partie (déjà fait dans updateUI)
                    }
                } else {
                    Log.e(TAG, "Erreur envoi mouvement: " + response.code() + " - " + response.message());
                    handleNetworkError("Erreur lors de l'envoi du mouvement. Code: " + response.code());
                    // Si erreur, on devrait peut-être retenter de récupérer l'état ?
                    fetchGameState(); // Pour resynchroniser
                }
            }

            @Override
            public void onFailure(@NonNull Call<GameStateResponse> call, @NonNull Throwable t) {
                Log.e(TAG, "Échec envoi mouvement", t);
                showLoading(false);
                handleNetworkError("Échec connexion pour envoi mouvement.");
                // Retenter de récupérer l'état ?
                fetchGameState();
            }
        });
    }


    private void showLoading(boolean show) {
        loadingIndicatorMulti.setVisibility(show ? View.VISIBLE : View.GONE);
    }

    private void handleNetworkError(String message) {
        statusTextMulti.setText(message);
        // Optionnel: Afficher un Toast aussi
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    /** Énumération interne pour les directions (peut être partagée avec MainActivity). */
    private enum Direction { UP, DOWN, LEFT, RIGHT }

} // Fin MultiplayerActivity