package com.example.boidelov3.game;

import com.example.boidelov3.Question;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Tests unitaires pour la classe GameEngine.
 */
public class GameEngineTest {

    private GameEngine gameEngine;
    private List<String> players;

    @Before
    public void setUp() {
        gameEngine = new GameEngine();
        players = Arrays.asList("Alice", "Bob", "Charlie", "David");
    }

    @Test
    public void testSelectRandomPlayers_returnsCorrectCount() {
        List<String> selected = gameEngine.selectRandomPlayers(players, 2);

        assertEquals("Should select 2 players", 2, selected.size());
        assertTrue("Should contain players from original list", players.containsAll(selected));
    }

    @Test
    public void testSelectRandomPlayers_returnsUniquePlayers() {
        List<String> selected = gameEngine.selectRandomPlayers(players, 3);

        // Vérifier l'unicité
        assertEquals("Should have 3 unique players", 3, new ArrayList<>(selected).size());
    }

    @Test
    public void testSelectRandomPlayers_handlesCountLargerThanList() {
        List<String> selected = gameEngine.selectRandomPlayers(players, 10);

        assertEquals("Should select max available players", 4, selected.size());
    }

    @Test
    public void testProcessQuestion_replacesJ1Placeholder() {
        Question question = createQuestion("<J1> doit boire 2 gorgées");
        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);

        String text = processed.question.getQuestion();
        assertFalse("Should not contain <J1> placeholder", text.contains("<J1>"));
        assertTrue("Should contain a player name", containsAnyPlayer(text, players));
    }

    @Test
    public void testProcessQuestion_replacesMultiplePlayerPlaceholders() {
        Question question = createQuestion("<J1> et <J2> boivent ensemble");
        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);

        String text = processed.question.getQuestion();
        assertFalse("Should not contain <J1>", text.contains("<J1>"));
        assertFalse("Should not contain <J2>", text.contains("<J2>"));
    }

    @Test
    public void testProcessQuestion_replacesVariante() {
        Question question = createQuestion("C'est une question <variante> !");
        question.setVariante(Arrays.asList("simple", "compliquée", "drôle"));

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertFalse("Should not contain <variante>", text.contains("<variante>"));
        assertTrue("Should contain one of the variantes",
                text.contains("simple") || text.contains("compliquée") || text.contains("drôle"));
    }

    @Test
    public void testProcessQuestion_handlesDistribution() {
        Question question = createQuestion("Question de test");
        question.setDistribution(true);
        question.setGorger(3);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertTrue("Should contain 'distribue'", text.contains("distribue"));
        assertTrue("Should contain gorgée count", text.contains("3"));
    }

    @Test
    public void testProcessQuestion_handlesRecois() {
        Question question = createQuestion("<J1> Question de test");
        question.setRecois(true);
        question.setGorger(2);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertTrue("Should contain 'boit'", text.contains("boit"));
        assertTrue("Should contain gorgée count", text.contains("2"));
    }

    @Test
    public void testProcessQuestion_addsExtraGorgees() {
        Question question = createQuestion("Question de test");
        question.setDistribution(true);
        question.setGorger(2);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 3);
        String text = processed.question.getQuestion();

        assertTrue("Should contain total gorgée count (2+3=5)", text.contains("5"));
    }

    @Test
    public void testProcessQuestion_handlesManches() {
        Question question = createQuestion("Défi à manches <manches> !");
        question.setArret("Arrêtez maintenant !");

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);

        assertTrue("Should be marked as manche", processed.isManche);
        assertTrue("Should have active manche", gameEngine.hasActiveManche());
        assertNotNull("Manche should have end message", processed.question.getArretMessageManche());
    }

    @Test
    public void testUpdateManches_decrementsMancheCount() {
        // Créer une manche
        Question question = createQuestion("Défi <manches>");
        question.setArret("Fin !");
        gameEngine.processQuestion(question, players, 0);

        // Récupérer le nombre initial
        GameEngine.MancheState initialState = gameEngine.updateManches();
        int initialCount = initialState.activeManche.getManchesRestantes();

        // Mettre à jour
        GameEngine.MancheState updatedState = gameEngine.updateManches();

        assertEquals("Manche count should decrease", initialCount - 1, updatedState.activeManche.getManchesRestantes());
    }

    @Test
    public void testClearManches_removesAllManches() {
        // Ajouter des manches
        Question question = createQuestion("Défi <manches>");
        question.setArret("Fin !");
        gameEngine.processQuestion(question, players, 0);

        gameEngine.clearManches();

        assertFalse("Should have no active manches", gameEngine.hasActiveManche());
        assertEquals("Should have 0 active manches", 0, gameEngine.getActiveManchesCount());
    }

    @Test
    public void testProcessQuestion_handlesPluralGorgees() {
        Question question = createQuestion("Test");
        question.setDistribution(true);
        question.setGorger(2);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertTrue("Should use plural 'gorgées'", text.contains("gorgées"));
    }

    @Test
    public void testProcessQuestion_handlesSingularGorgee() {
        Question question = createQuestion("Test");
        question.setDistribution(true);
        question.setGorger(1);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertTrue("Should use singular 'gorgée'", text.contains("gorgée"));
    }

    // Helper methods

    private Question createQuestion(String text) {
        Question q = new Question();
        q.setId(1);
        q.setQuestion(text);
        return q;
    }

    private boolean containsAnyPlayer(String text, List<String> players) {
        for (String player : players) {
            if (text.contains(player)) {
                return true;
            }
        }
        return false;
    }

    // Tests supplémentaires pour une meilleure couverture

    @Test
    public void testSelectRandomPlayers_withSinglePlayer() {
        List<String> singlePlayer = Arrays.asList("Alice");
        List<String> selected = gameEngine.selectRandomPlayers(singlePlayer, 1);

        assertEquals("Should select 1 player", 1, selected.size());
        assertEquals("Should be Alice", "Alice", selected.get(0));
    }

    @Test
    public void testProcessQuestion_withBothRecoisAndDistribution() {
        Question question = createQuestion("<J1> Test");
        question.setRecois(true);
        question.setDistribution(true);
        question.setGorger(2);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        // Should contain either "boit" or "distribue" (random choice)
        boolean containsBoit = text.contains("boit");
        boolean containsDistribue = text.contains("distribue");
        assertTrue("Should contain either 'boit' or 'distribue'", containsBoit || containsDistribue);
    }

    @Test
    public void testProcessQuestion_withNoGorgeesFlags() {
        Question question = createQuestion("Question simple sans modification");
        question.setRecois(false);
        question.setDistribution(false);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertFalse("Should not contain 'bois'", text.contains("bois"));
        assertFalse("Should not contain 'distribue'", text.contains("distribue"));
        assertFalse("Should not contain 'gorgée'", text.toLowerCase().contains("gorgée"));
    }

    @Test
    public void testUpdateManches_withNoActiveManches() {
        GameEngine.MancheState state = gameEngine.updateManches();

        assertNull("Active manche should be null", state.activeManche);
        assertFalse("Should not have manche", state.hasManche);
        assertNull("End message should be null", state.endMessage);
    }

    @Test
    public void testProcessQuestion_mancheDecrementsCorrectly() {
        Question question = createQuestion("Défi <manches>");
        question.setArret("Fin !");

        gameEngine.processQuestion(question, players, 0);

        // Get initial state
        GameEngine.MancheState state1 = gameEngine.updateManches();
        int count1 = state1.activeManche.getManchesRestantes();

        // Update again
        GameEngine.MancheState state2 = gameEngine.updateManches();
        int count2 = state2.activeManche.getManchesRestantes();

        assertEquals("Manche should decrement by 1", count1 - 1, count2);
    }

    @Test
    public void testProcessQuestion_mancheFinishes_returnsEndMessage() {
        Question question = createQuestion("Défi <manches>");
        question.setArret("Bravo !");

        gameEngine.processQuestion(question, players, 0);

        // Update until manche ends (1 left -> 0)
        GameEngine.MancheState state;
        do {
            state = gameEngine.updateManches();
        } while (state.hasManche);

        assertNotNull("Should have end message", state.endMessage);
        assertTrue("End message should contain stop message",
                state.endMessage.contains("Fin de défi!") || state.endMessage.contains("Bravo !"));
    }

    @Test
    public void testGetActiveManchesCount_incrementsWithManches() {
        assertEquals("Initial count should be 0", 0, gameEngine.getActiveManchesCount());

        Question q1 = createQuestion("Défi 1 <manches>");
        q1.setArret("Fin 1");
        gameEngine.processQuestion(q1, players, 0);

        assertEquals("Count should be 1", 1, gameEngine.getActiveManchesCount());

        Question q2 = createQuestion("Défi 2 <manches>");
        q2.setArret("Fin 2");
        gameEngine.processQuestion(q2, players, 0);

        assertEquals("Count should be 2", 2, gameEngine.getActiveManchesCount());
    }

    @Test
    public void testClearManches_afterMultipleManches() {
        Question q1 = createQuestion("Défi 1 <manches>");
        q1.setArret("Fin 1");
        gameEngine.processQuestion(q1, players, 0);

        Question q2 = createQuestion("Défi 2 <manches>");
        q2.setArret("Fin 2");
        gameEngine.processQuestion(q2, players, 0);

        assertTrue("Should have active manches", gameEngine.hasActiveManche());

        gameEngine.clearManches();

        assertFalse("Should have no active manches", gameEngine.hasActiveManche());
        assertEquals("Count should be 0", 0, gameEngine.getActiveManchesCount());
    }

    @Test
    public void testProcessQuestion_preservesOriginalQuestion() {
        Question original = createQuestion("<J1> bois 2 gorgées");
        original.setGorger(2);
        original.setRecois(true);

        String originalText = original.getQuestion();

        gameEngine.processQuestion(original, players, 0);

        assertEquals("Original question should be unchanged", originalText, original.getQuestion());
    }

    @Test
    public void testProcessQuestion_withEmptyVarianteList() {
        Question question = createQuestion("Question <variante>");
        question.setVariante(Arrays.asList());

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        // Should not replace variante if list is empty
        assertTrue("Should still contain <variante> tag", text.contains("<variante>"));
    }

    @Test
    public void testSelectRandomPlayers_returnsSameSizeWhenRequestedMore() {
        List<String> smallList = Arrays.asList("A", "B");
        List<String> selected = gameEngine.selectRandomPlayers(smallList, 5);

        assertEquals("Should return max available", 2, selected.size());
    }

    @Test
    public void testProcessQuestion_mancheWithArretNull() {
        Question question = createQuestion("Défi <manches>");
        question.setArret(null);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);

        assertTrue("Should be a manche", processed.isManche);
        assertNotNull("Should have default end message", processed.question.getArretMessageManche());
    }

    @Test
    public void testProcessQuestion_withZeroAddedGorgees() {
        Question question = createQuestion("Test");
        question.setDistribution(true);
        question.setGorger(3);

        GameEngine.ProcessedQuestion processed = gameEngine.processQuestion(question, players, 0);
        String text = processed.question.getQuestion();

        assertTrue("Should contain base gorgées (3)", text.contains("3"));
    }

    @Test
    public void testHasActiveManche_initiallyFalse() {
        assertFalse("Should not have active manche initially", gameEngine.hasActiveManche());
    }
}
