package it.polimi.ingsw.gc14.Model;

import it.polimi.ingsw.gc14.Model.Cards.BuildingCard;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.CharacterType;
import it.polimi.ingsw.gc14.Model.Cards.TribeCards.Characters.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

class PlayerTest {

    @Test
    void getUserName(){
        String usr = "test";
        Player p  = new Player(usr);
        assertEquals(usr, p.getUserName());
        assertTrue(p.getUserName().length() <= 32);
        assertTrue(p.getUserName().length() > 0);
    }

    @Test
    void getTotCharacters(){
        String usr = "test";
        Player p  = new Player(usr);
        assertEquals(0, p.getTotCharacters());
        int N = 10;
        ArrayList<Artist> a = Stream.generate(() -> new Artist(1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.artists.addAll(a);
        assertEquals(N, p.getTotCharacters());
        ArrayList<Builder> b = Stream.generate(() -> new Builder(1, 1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.builders.addAll(b);
        assertEquals(2 * N, p.getTotCharacters());
    }

    @Test
    void getNType(){
        String usr = "test";
        Player p  = new Player(usr);
        for(CharacterType type : CharacterType.values()){
            assertEquals(0, p.getNType(type));
        }
        int N = 10;
        ArrayList<Artist> a = Stream.generate(() -> new Artist(1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.artists.addAll(a);

        ArrayList<Builder> b = Stream.generate(() -> new Builder(1, 1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.builders.addAll(b);

        ArrayList<Inventor> i = Stream.generate(() -> new Inventor(1, 1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.inventors.addAll(i);

        ArrayList<Gatherer> g = Stream.generate(() -> new Gatherer(1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.gatherers.addAll(g);

        ArrayList<Shaman> s = Stream.generate(() -> new Shaman(1, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.shamans.addAll(s);

        ArrayList<Hunter> h = Stream.generate(() -> new Hunter(1, true, 1)).limit(N).collect(Collectors.toCollection(ArrayList::new));
        p.hunters.addAll(h);

        assertEquals(N, p.getNType(CharacterType.ARTIST));
        assertEquals(N, p.getNType(CharacterType.BUILDER));
        assertEquals(N, p.getNType(CharacterType.INVENTOR));
        assertEquals(N, p.getNType(CharacterType.GATHERER));
        assertEquals(N, p.getNType(CharacterType.SHAMAN));
        assertEquals(N, p.getNType(CharacterType.HUNTER));
    }

    @Test
    void getFoodValue(){
        int food = 5;
        Player p = new Player("test");
        assertEquals(0, p.getFoodValue());
        p.addFood(food);
        assertEquals(food, p.getFoodValue());
        p.removeFood(10 * food);
        assertEquals(food, p.getFoodValue());
        p.removeFood(food);
        assertEquals(0, p.getFoodValue());
    }

    @Test
    void getPrestigeValue(){
        int prestige = 5;
        Player p = new Player("test");
        assertEquals(0, p.getPrestigeValue());
        p.addPrestige(prestige);
        assertEquals(prestige, p.getPrestigeValue());
        p.removePrestige(prestige);
        assertEquals(0, p.getPrestigeValue());
        p.removePrestige(prestige);
        assertEquals(-prestige, p.getPrestigeValue());
    }

    @Test
    void addFood() {
        Player p = new Player("test");
        int old_val = p.getFoodValue();
        p.addFood(0);
        int new_val = p.getFoodValue();
        assertEquals(old_val, new_val);
        p.addFood(1);
        new_val = p.getFoodValue();
        assertNotEquals(old_val, new_val);
        p.addFood(-2);
        new_val = p.getFoodValue();
        assertEquals(-1, new_val);
        p.addFood(1);

        p.addFood(3);
        p.removeFood(3);
        assertEquals(0, p.getFoodValue());

        p.addFood(4);
        p.removeFood(p.getFoodValue());
        assertEquals(0, p.getFoodValue());
    }

    @Test
    void removeFood() {
        Player p = new Player("test");
        assertEquals(true, p.removeFood(0));
        assertEquals(false, p.removeFood(1));
        assertEquals(true, p.removeFood(-1));
    }

    @Test
    void addPrestige() {
        Player p = new Player("test");
        p.addPrestige(0);
        assertEquals(0, p.getPrestigeValue());
        p.addPrestige(1);
        assertEquals(1, p.getPrestigeValue());
        p.addPrestige(-3);
        assertEquals(-2, p.getPrestigeValue());
    }

    @Test
    void removePrestige() {
        Player p = new Player("test");
        p.removePrestige(0);
        assertEquals(0, p.getPrestigeValue());
        p.removePrestige(1);
        assertEquals(-1, p.getPrestigeValue());
        p.removePrestige(-3);
        assertEquals(2, p.getPrestigeValue());
    }


    @Test
    void setUserName() {
        String UserName = "abcdabcdabcdabcdabcdabcdabcdabcd";
        Player p = new Player(UserName);
        assertEquals(0, p.getFoodValue());
        assertEquals(0, p.getPrestigeValue());
        assertEquals(32, p.getUserName().length());
        String username = "abcdabcdabcda";
        p = new Player(username);

        //Constructor default values testing
        assertEquals(username, p.getUserName());
        assertEquals(0, p.getFoodValue());
        assertEquals(0, p.getPrestigeValue());
        assertTrue(p.inventors.isEmpty());
        assertTrue(p.builders.isEmpty());
        assertTrue(p.gatherers.isEmpty());
        assertTrue(p.shamans.isEmpty());
        assertTrue(p.hunters.isEmpty());
        assertTrue(p.artists.isEmpty());
        assertTrue(p.buildingCards.isEmpty());

        assertTrue(p.getUserName().length() <= 32);
        assertTrue(p.getUserName().length() > 0);
    }

    @Test
    @DisplayName("Exception Test Max Length")
    void test_max() {
        assertThrows(IllegalArgumentException.class, () -> {
            Player p = new Player("abcdabcdabcdabcdabcdabcdabcdabcdZ");
        });
    }

    @Test
    @DisplayName("Exception Test Min Length")
    void test_min() {
        assertThrows(IllegalArgumentException.class, () -> {
            Player p = new Player("");
        });
    }

    @Test
    void tostring() {
        Player p = new Player("test_usr");
        p.addPrestige(15);
        p.addFood(7);
        p.artists.add(new Artist(1));
        p.artists.add(new Artist(2));
        p.buildingCards.add(new BuildingCard(2, 1,2, 5));
        p.buildingCards.add(new BuildingCard(2, 2,3, 6));
        assertEquals("test_usr:"+ "\n\t" +
                "Food: 7" + "\n\t" +
                "Prestige: 15" + "\n\t" +
                "Artists: [Era:1 ARTIST, Era:2 ARTIST]" +  "\n\t" + "Builders: []" +  "\n\t" +
                "Gatherers: []" +  "\n\t" +
                "Shamans: []" +  "\n\t" +
                "Hunters: []" +  "\n\t" +
                "Buildings: [Era:1 Price:2 Prestige:5, Era:2 Price:3 Prestige:6]",
                p.toString());
    }
}