package com.example.boidelov3.games.game89;

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

import java.util.HashSet;
import java.util.Set;

/**
 * Tests unitaires pour la classe Game89ChallengeManager.
 * Couvre la gestion des défis, la sélection aléatoire et l'affichage.
 */
public class Game89ChallengeManagerTest {

    private Game89ChallengeManager challengeManager;

    @Before
    public void setUp() {
        challengeManager = new Game89ChallengeManager();
    }

    @Test
    public void testConstructor_initializesChallenges() {
        assertNotNull("ChallengeManager should not be null", challengeManager);
    }

    @Test
    public void testGetRandomChallenge_returnsNonNull() {
        Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();

        assertNotNull("Random challenge should not be null", challenge);
    }

    @Test
    public void testGetRandomChallenge_returnsValidChallenge() {
        Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();

        assertNotNull("Challenge type should not be null", challenge.getType());
        assertNotNull("Challenge title should not be null", challenge.getTitle());
        assertNotNull("Challenge description should not be null", challenge.getDescription());
        assertFalse("Challenge title should not be empty", challenge.getTitle().isEmpty());
        assertFalse("Challenge description should not be empty", challenge.getDescription().isEmpty());
    }

    @Test
    public void testGetRandomChallenge_multipleCalls_returnsDifferentTypes() {
        Set<Game89ChallengeManager.ChallengeType> seenTypes = new HashSet<>();

        // Run multiple times to potentially see different challenges
        for (int i = 0; i < 50; i++) {
            Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();
            seenTypes.add(challenge.getType());
        }

        // Should see at least 3 different types (not guaranteed to see all, but likely)
        assertTrue("Should see multiple challenge types", seenTypes.size() >= 3);
    }

    @Test
    public void testChallenge_allTypesHaveUniqueTitles() {
        Set<String> titles = new HashSet<>();
        Set<Game89ChallengeManager.ChallengeType> seenTypes = new HashSet<>();

        // Collect all unique challenges
        for (int i = 0; i < 100; i++) {
            Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();
            titles.add(challenge.getTitle());
            seenTypes.add(challenge.getType());
        }

        // Should have as many unique titles as types
        assertEquals("Each challenge type should have unique title",
                seenTypes.size(), titles.size());
    }

    @Test
    public void testChallengeType_enumValues() {
        Game89ChallengeManager.ChallengeType[] types = Game89ChallengeManager.ChallengeType.values();

        assertEquals("Should have 9 challenge types", 9, types.length);

        // Verify expected types exist
        Set<String> expectedNames = new HashSet<>();
        expectedNames.add("REVERSE_DIRECTION");
        expectedNames.add("IMMUNITY");
        expectedNames.add("DRINK_GORGEE");
        expectedNames.add("SKIP_TURN");
        expectedNames.add("SWAP_HAND");
        expectedNames.add("DOUBLE_NEXT");
        expectedNames.add("RANDOM_COUNT");
        expectedNames.add("EVERYONE_DRINKS");
        expectedNames.add("PICK_VICTIM");

        for (Game89ChallengeManager.ChallengeType type : types) {
            assertTrue("Type should be in expected list: " + type.name(),
                    expectedNames.contains(type.name()));
        }
    }

    @Test
    public void testGetChallengeDisplay_withNullChallenge() {
        String display = Game89ChallengeManager.getChallengeDisplay(null);

        assertEquals("Null challenge should return empty string", "", display);
    }

    @Test
    public void testGetChallengeDisplay_withValidChallenge() {
        Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);

        assertNotNull("Display should not be null", display);
        assertTrue("Display should contain title", display.contains(challenge.getTitle()));
        assertTrue("Display should contain description", display.contains(challenge.getDescription()));
    }

    @Test
    public void testGetChallengeDisplay_formatIsCorrect() {
        Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);

        // Should have title, newline, then description
        String expectedFormat = challenge.getTitle() + "\n" + challenge.getDescription();
        assertEquals("Display format should be title\\ndescription", expectedFormat, display);
    }

    @Test
    public void testChallenge_gettersReturnCorrectValues() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.DRINK_GORGEE,
                "Test Title",
                "Test Description"
        );

        assertEquals("Type should match", Game89ChallengeManager.ChallengeType.DRINK_GORGEE, challenge.getType());
        assertEquals("Title should match", "Test Title", challenge.getTitle());
        assertEquals("Description should match", "Test Description", challenge.getDescription());
    }

    @Test
    public void testChallenge_withEmptyStrings() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.SKIP_TURN,
                "",
                ""
        );

        assertEquals("Title should be empty", "", challenge.getTitle());
        assertEquals("Description should be empty", "", challenge.getDescription());
    }

    @Test
    public void testChallenge_withSpecialCharacters() {
        String specialTitle = "Défi !@#$%^&*()";
        String specialDescription = "Description avec émojis 🎮🎯";

        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.IMMUNITY,
                specialTitle,
                specialDescription
        );

        assertEquals("Should handle special characters in title", specialTitle, challenge.getTitle());
        assertEquals("Should handle special characters in description", specialDescription, challenge.getDescription());

        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain special characters", display.contains("🎮"));
    }

    @Test
    public void testMultipleChallengeManagers_areIndependent() {
        Game89ChallengeManager manager1 = new Game89ChallengeManager();
        Game89ChallengeManager manager2 = new Game89ChallengeManager();

        // Get random challenges from both
        Game89ChallengeManager.Challenge challenge1 = manager1.getRandomChallenge();
        Game89ChallengeManager.Challenge challenge2 = manager2.getRandomChallenge();

        // Both should return valid challenges
        assertNotNull("Manager1 should return challenge", challenge1);
        assertNotNull("Manager2 should return challenge", challenge2);
    }

    @Test
    public void testRandomDistribution_isFair() {
        // Count occurrences of each challenge type
        java.util.Map<Game89ChallengeManager.ChallengeType, Integer> counts = new java.util.HashMap<>();

        for (Game89ChallengeManager.ChallengeType type : Game89ChallengeManager.ChallengeType.values()) {
            counts.put(type, 0);
        }

        // Run many times
        int iterations = 1000;
        for (int i = 0; i < iterations; i++) {
            Game89ChallengeManager.Challenge challenge = challengeManager.getRandomChallenge();
            counts.put(challenge.getType(), counts.get(challenge.getType()) + 1);
        }

        // Each type should appear at least once (statistically very likely with 1000 iterations)
        for (Game89ChallengeManager.ChallengeType type : Game89ChallengeManager.ChallengeType.values()) {
            assertTrue("Type " + type.name() + " should appear at least once",
                    counts.get(type) > 0);
        }

        // No type should appear excessively (>50% of the time)
        for (Game89ChallengeManager.ChallengeType type : Game89ChallengeManager.ChallengeType.values()) {
            assertTrue("Type " + type.name() + " should not appear more than 50% of time",
                    counts.get(type) < iterations * 0.5);
        }
    }

    @Test
    public void testChallengeType_valuesAreUnique() {
        Game89ChallengeManager.ChallengeType[] types = Game89ChallengeManager.ChallengeType.values();
        Set<Game89ChallengeManager.ChallengeType> typeSet = new java.util.HashSet<>();

        for (Game89ChallengeManager.ChallengeType type : types) {
            assertTrue("Type should be unique: " + type.name(), typeSet.add(type));
        }
    }

    @Test
    public void testChallenge_ImmunityType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.IMMUNITY,
                "Immunité !",
                "Le joueur actuel est immunisé contre les gorgées pendant 5 minutes !"
        );

        assertEquals("Should be IMMUNITY type", Game89ChallengeManager.ChallengeType.IMMUNITY, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'immunisé'", display.contains("immunisé"));
    }

    @Test
    public void testChallenge_EveryoneDrinksType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.EVERYONE_DRINKS,
                "Tour générale !",
                "Tout le monde boit 2 gorgées !"
        );

        assertEquals("Should be EVERYONE_DRINKS type",
                Game89ChallengeManager.ChallengeType.EVERYONE_DRINKS, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'Tout le monde'", display.contains("Tout le monde"));
    }

    @Test
    public void testChallenge_ReverseDirectionType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.REVERSE_DIRECTION,
                "Changement de sens !",
                "Le sens du jeu est inversé !"
        );

        assertEquals("Should be REVERSE_DIRECTION type",
                Game89ChallengeManager.ChallengeType.REVERSE_DIRECTION, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should mention 'sens'", display.contains("sens"));
    }

    @Test
    public void testChallenge_SwapHandType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.SWAP_HAND,
                "Échange de mains !",
                "Le joueur actuel échange sa main avec le joueur de son choix !"
        );

        assertEquals("Should be SWAP_HAND type",
                Game89ChallengeManager.ChallengeType.SWAP_HAND, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'échange'", display.toLowerCase().contains("échange"));
    }

    @Test
    public void testChallenge_DoubleNextType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.DOUBLE_NEXT,
                "Double !",
                "La prochaine carte jouée compte double !"
        );

        assertEquals("Should be DOUBLE_NEXT type",
                Game89ChallengeManager.ChallengeType.DOUBLE_NEXT, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'double'", display.toLowerCase().contains("double"));
    }

    @Test
    public void testChallenge_RandomCountType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.RANDOM_COUNT,
                "Compte mystère !",
                "Ajoutez un nombre aléatoire entre 1 et 10 au compteur !"
        );

        assertEquals("Should be RANDOM_COUNT type",
                Game89ChallengeManager.ChallengeType.RANDOM_COUNT, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should mention 'aléatoire'", display.contains("aléatoire"));
    }

    @Test
    public void testChallenge_PickVictimType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.PICK_VICTIM,
                "Victime !",
                "Le joueur actuel choisit quelqu'un qui boit 3 gorgées !"
        );

        assertEquals("Should be PICK_VICTIM type",
                Game89ChallengeManager.ChallengeType.PICK_VICTIM, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'victime' or 'choisit'",
                display.toLowerCase().contains("victime") || display.toLowerCase().contains("choisit"));
    }

    @Test
    public void testChallenge_SkipTurnType() {
        Game89ChallengeManager.Challenge challenge = new Game89ChallengeManager.Challenge(
                Game89ChallengeManager.ChallengeType.SKIP_TURN,
                "Joker !",
                "Le joueur actuel passe son tour !"
        );

        assertEquals("Should be SKIP_TURN type",
                Game89ChallengeManager.ChallengeType.SKIP_TURN, challenge.getType());
        String display = Game89ChallengeManager.getChallengeDisplay(challenge);
        assertTrue("Display should contain 'passe'", display.contains("passe"));
    }
}
