package it.polimi.ingsw.gc14.Controller;

import it.polimi.ingsw.gc14.Model.Cards.TribeCard;
import it.polimi.ingsw.gc14.Model.Game;
import it.polimi.ingsw.gc14.Model.GamePackage.GameStages;
import it.polimi.ingsw.gc14.Model.Player;
import org.junit.jupiter.api.Test;

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import static org.junit.jupiter.api.Assertions.*;

class GameControllerTest {

    @Test
    void addPlayer() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertTrue(controller.addPlayer("Giorgio"));
        assertTrue(controller.addPlayer("Marco"));
        assertTrue(controller.addPlayer("Luca"));

        assertNotNull(game.getPlayerByUsername("Giorgio"));
        assertNotNull(game.getPlayerByUsername("Marco"));
        assertNotNull(game.getPlayerByUsername("Luca"));

        assertEquals(GameStages.SLOT_CHOICE, game.getCurrentState().getGameStage());
        assertFalse(controller.addPlayer("Extra"));
    }

    @Test
    void allMethodsShouldReturnFalseForUnknownUsername() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertFalse(controller.slotChoice("ghost", 0));
        assertFalse(controller.drawUpperTribeCard("ghost", 0));
        assertFalse(controller.drawLowerTribeCard("ghost", 0));
        assertFalse(controller.drawUpperBuildingCard("ghost", 0));
        assertFalse(controller.drawLowerBuildingCard("ghost", 0));
        assertFalse(controller.pickOptionalTribeCard("ghost", 0));
        assertFalse(controller.pickOptionalBuildingCard("ghost", 0));
    }

    @Test
    void slotChoice() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertTrue(controller.addPlayer("Giorgio"));
        assertTrue(controller.addPlayer("Marco"));
        assertTrue(controller.addPlayer("Luca"));

        String cur = game.getCurrentState().getCurrentPlayer().getUserName();
        String other = cur.equals("Giorgio") ? "Marco" : "Giorgio";
        assertFalse(controller.slotChoice(other, 0));

        Queue<Player> order = new LinkedList<>();
        for (int i = 0; i < 3; i++)  {
            Player p = game.getCurrentState().getCurrentPlayer();
            order.add(p);
            assertTrue(controller.slotChoice(p.getUserName(), i));
        }

        assertEquals(GameStages.RESOLVING_ACTIONS, game.getCurrentState().getGameStage());
        assertEquals(order.poll(), game.getCurrentState().getCurrentPlayer());
    }

    @Test
    void drawLowerTribeCard() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertTrue(controller.addPlayer("Giorgio"));
        assertTrue(controller.addPlayer("Marco"));
        assertTrue(controller.addPlayer("Luca"));

        Queue<Player> order = new LinkedList<>();
        for (int i = 0; i < 3; i++) {
            Player p = game.getCurrentState().getCurrentPlayer();
            order.add(p);
            assertTrue(controller.slotChoice(p.getUserName(), i));
        }

        Player first = order.poll();
        assertEquals(first, game.getCurrentState().getCurrentPlayer());

        List<TribeCard> cards = game.getLowerListTribeCards();
        int idx = cards.indexOf(
                cards.stream()
                        .filter(c -> !c.IsEventCard())
                        .findFirst()
                        .orElseThrow()
        );

        Player wrongPlayer = order.peek();
        assertNotNull(wrongPlayer);

        int before = first.getTotCharacters();
        assertTrue(controller.drawLowerTribeCard(first.getUserName(), idx));
        assertEquals(before + 1, first.getTotCharacters());
    }

    @Test
    void drawUpperTribeCard() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertTrue(controller.addPlayer("Giorgio"));
        assertTrue(controller.addPlayer("Marco"));
        assertTrue(controller.addPlayer("Luca"));

        for (int i = 0; i < 3; i++) {
            Player p = game.getCurrentState().getCurrentPlayer();
            assertTrue(controller.slotChoice(p.getUserName(), i));
        }

        assertEquals(GameStages.RESOLVING_ACTIONS, game.getCurrentState().getGameStage());

        while (game.getCurrentState().getNUpper() == 0) {
            assertTrue(game.getCurrentState().getNLower() > 0);

            Player current = game.getCurrentState().getCurrentPlayer();
            List<TribeCard> lower = game.getLowerListTribeCards();
            int lowerIdx = lower.indexOf(
                    lower.stream()
                            .filter(c -> !c.IsEventCard())
                            .findFirst()
                            .orElseThrow()
            );

            assertTrue(controller.drawLowerTribeCard(current.getUserName(), lowerIdx));
        }

        Player current = game.getCurrentState().getCurrentPlayer();
        int beforeTot = current.getTotCharacters();

        List<TribeCard> upper = game.getUpperListTribeCards();
        int upperIdx = upper.indexOf(
                upper.stream()
                        .filter(c -> !c.IsEventCard())
                        .findFirst()
                        .orElseThrow()
        );

        assertTrue(controller.drawUpperTribeCard(current.getUserName(), upperIdx));
        assertEquals(beforeTot + 1, current.getTotCharacters());
    }

    @Test
    void drawUpperBuildingCard() {
        Game game = new Game(3);
        GameController controller = new GameController(game);

        assertTrue(controller.addPlayer("Giacomo"));
        assertTrue(controller.addPlayer("Marco"));
        assertTrue(controller.addPlayer("Luca"));

        for (int i = 0; i < 3; i++) {
            Player p = game.getCurrentState().getCurrentPlayer();
            assertTrue(controller.slotChoice(p.getUserName(), i));
        }

        assertEquals(GameStages.RESOLVING_ACTIONS, game.getCurrentState().getGameStage());

        while (game.getCurrentState().getNUpper() == 0) {
            assertTrue(game.getCurrentState().getNLower() > 0);

            Player current = game.getCurrentState().getCurrentPlayer();
            List<TribeCard> lower = game.getLowerListTribeCards();
            int lowerIdx = lower.indexOf(
                    lower.stream()
                            .filter(c -> !c.IsEventCard())
                            .findFirst()
                            .orElseThrow()
            );

            assertTrue(controller.drawLowerTribeCard(current.getUserName(), lowerIdx));
        }

        Player current = game.getCurrentState().getCurrentPlayer();

        assertFalse(controller.drawUpperBuildingCard(current.getUserName(), 0));

        current.addFood(100);
        assertTrue(controller.drawUpperBuildingCard(current.getUserName(), 0));
    }

        @Test
        void drawLowerBuildingCard() {
            Game game = new Game(3);
            GameController controller = new GameController(game);

            assertTrue(controller.addPlayer("Giacomo"));
            assertTrue(controller.addPlayer("Marco"));
            assertTrue(controller.addPlayer("Luca"));

            for (int i = 0; i < 3; i++) {
                Player p = game.getCurrentState().getCurrentPlayer();
                assertTrue(controller.slotChoice(p.getUserName(), i));
            }

            String cur = game.getCurrentState().getCurrentPlayer().getUserName();
            assertFalse(controller.drawLowerBuildingCard(cur, 0));
    }

        @Test
        void pickOptionalCards() {
            Game game = new Game(3);
            GameController controller = new GameController(game);

            assertTrue(controller.addPlayer("Giacomo"));
            assertTrue(controller.addPlayer("Marco"));
            assertTrue(controller.addPlayer("Luca"));

            for (int i = 0; i < 3; i++) {
                Player p = game.getCurrentState().getCurrentPlayer();
                assertTrue(controller.slotChoice(p.getUserName(), i));
            }

            String cur = game.getCurrentState().getCurrentPlayer().getUserName();
            assertFalse(controller.pickOptionalTribeCard(cur, 0));
            assertFalse(controller.pickOptionalBuildingCard(cur, 0));
    }
}
