package it.polimi.ingsw.gc14.Model.Orders;

import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
import it.polimi.ingsw.gc14.Model.Player;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.NoSuchElementException;
import static org.junit.jupiter.api.Assertions.*;

class Order4Test {

    @Test
    @DisplayName("Testing Order4 constructor with wrong number of player")
    void constructorTest() {
        Player p1 = new Player("p1");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        assertThrows(NoSuchElementException.class, () -> {new Order4(players);});
    }

    @Test
    void firstReturnGetsFood() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        order.pull();
        order.pull();
        order.pull();

        int initialFood = firstToAct.getFoodValue();
        order.push(firstToAct);

        assertEquals(initialFood + 2, firstToAct.getFoodValue());
        assertEquals(firstToAct, order.getFirst());
    }

    @Test
    void secondReturnGetsFood() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        Player secondToAct = order.pull();
        order.pull();
        order.pull();

        int initialFood = secondToAct.getFoodValue();

        order.push(firstToAct);
        order.push(secondToAct);

        assertEquals(initialFood + 1, secondToAct.getFoodValue());
    }

    @Test
    void thirdReturnNoEffect() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        Player secondToAct = order.pull();
        Player thirdToAct = order.pull();
        order.pull();

        int initialFood = thirdToAct.getFoodValue();
        int initialPrestige = thirdToAct.getPrestigeValue();

        order.push(firstToAct);
        order.push(secondToAct);
        order.push(thirdToAct);

        assertEquals(initialFood, thirdToAct.getFoodValue());
        assertEquals(initialPrestige, thirdToAct.getPrestigeValue());
    }

    @Test
    void fourthReturnPaysFoodAndLosesPrestige() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        Player secondToAct = order.pull();
        Player thirdToAct = order.pull();
        Player fourthToAct = order.pull();

        fourthToAct.addFood(1);

        order.push(firstToAct);
        order.push(secondToAct);
        order.push(thirdToAct);
        order.push(fourthToAct);

        assertEquals(0, fourthToAct.getFoodValue());
        assertEquals(-2, fourthToAct.getPrestigeValue());
    }

    @Test
    void fourthReturnWithoutFoodNoEffect() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        Player secondToAct = order.pull();
        Player thirdToAct = order.pull();
        Player fourthToAct = order.pull();

        int initialFood = fourthToAct.getFoodValue();
        int initialPrestige = fourthToAct.getPrestigeValue();

        order.push(firstToAct);
        order.push(secondToAct);
        order.push(thirdToAct);
        order.push(fourthToAct);

        assertEquals(initialFood, fourthToAct.getFoodValue());
        assertEquals(initialPrestige, fourthToAct.getPrestigeValue());
    }

    @Test
    void firstReturnWithBuilding() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        order.pull();
        order.pull();
        order.pull();

        firstToAct.buildingCards.add(new BuildingCard(3, 1, 1, 2));

        order.push(firstToAct);

        assertEquals(3, firstToAct.getFoodValue());
    }

    @Test
    void secondReturnWithBuilding() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        Player firstToAct = order.pull();
        Player secondToAct = order.pull();
        order.pull();
        order.pull();

        secondToAct.buildingCards.add(new BuildingCard(3, 1, 1, 2));

        order.push(firstToAct);
        order.push(secondToAct);

        assertEquals(2, secondToAct.getFoodValue());
    }

    @Test
    void fifthPlayerThrows() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        Player p5 = new Player("p5");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);
        Order4 order = new Order4(players);

        order.pull();
        order.pull();
        order.pull();
        order.pull();

        order.push(p1);
        order.push(p2);
        order.push(p3);
        order.push(p4);

        assertThrows(IndexOutOfBoundsException.class, () -> order.push(p5));
    }

    @Test
    void constructorLoadsPlayers() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        ArrayList<Player> players = new ArrayList<>();
        players.add(p1);
        players.add(p2);
        players.add(p3);
        players.add(p4);

        Order4 order = new Order4(players);

        Player firstOut = order.pull();
        Player secondOut = order.pull();
        Player thirdOut = order.pull();
        Player fourthOut = order.pull();

        assertTrue(firstOut == p1 || firstOut == p2 || firstOut == p3 || firstOut == p4);
        assertTrue(secondOut == p1 || secondOut == p2 || secondOut == p3 || secondOut == p4);
        assertTrue(thirdOut == p1 || thirdOut == p2 || thirdOut == p3 || thirdOut == p4);
        assertTrue(fourthOut == p1 || fourthOut == p2 || fourthOut == p3 || fourthOut == p4);

        assertNotEquals(firstOut, secondOut);
        assertNotEquals(firstOut, thirdOut);
        assertNotEquals(firstOut, fourthOut);
        assertNotEquals(secondOut, thirdOut);
        assertNotEquals(secondOut, fourthOut);
        assertNotEquals(thirdOut, fourthOut);

        assertNull(order.pull());
    }

    @Test
    void getFirst() {
        Player p1 = new Player("p1");
        Player p2 = new Player("p2");
        Player p3 = new Player("p3");
        Player p4 = new Player("p4");
        Order4 order = new Order4(new ArrayList<>(Arrays.asList(p1, p2, p3, p4)));

        order.pull();
        order.pull();
        order.pull();
        order.pull();
        assertNull(order.getFirst());

        order.push(p1);

        assertEquals(p1, order.getFirst());
        assertEquals(p1, order.getFirst());
    }
}