////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////

package pp.battleship.game.server;

import org.junit.Before;
import org.junit.Test;
import pp.battleship.BattleshipConfig;
import pp.battleship.message.client.MapMessage;
import pp.battleship.message.client.ShootMessage;
import pp.battleship.message.server.EffectMessage;
import pp.battleship.message.server.GameDetails;
import pp.battleship.message.server.StartBattleMessage;
import pp.battleship.model.Battleship;
import pp.battleship.model.IntPoint;
import pp.battleship.model.Rotation;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

public class ServerGameLogicTest {
    private ServerGameLogic serverGameLogic;
    private ServerSender mockServerSender;

    @Before
    public void setUp() {
        mockServerSender = mock(ServerSender.class);
        serverGameLogic = new ServerGameLogic(mockServerSender, new BattleshipConfig());
    }

    @Test
    public void testAddPlayer() {
        serverGameLogic.addPlayer(1);
        serverGameLogic.addPlayer(2);
        verify(mockServerSender, times(2)).send(anyInt(), any(GameDetails.class));
        assertEquals(ServerState.SET_UP, serverGameLogic.getState());
    }

    @Test
    public void testGetPlayerById() {
        final Player player1 = serverGameLogic.addPlayer(1);
        final Player player2 = serverGameLogic.addPlayer(2);
        assertEquals(player1, serverGameLogic.getPlayerById(1));
        assertEquals(player2, serverGameLogic.getPlayerById(2));
    }

    @Test
    public void testReceivedMapMessage() {
        serverGameLogic.addPlayer(1);
        serverGameLogic.addPlayer(2);
        List<Battleship> ships = List.of(new Battleship(3, 2, 2, Rotation.RIGHT));
        MapMessage mapMessage = new MapMessage(ships);
        serverGameLogic.received(mapMessage, 1);
        serverGameLogic.received(mapMessage, 2);
        verify(mockServerSender, times(2)).send(anyInt(), any(StartBattleMessage.class));
        assertEquals(ServerState.BATTLE, serverGameLogic.getState());
    }

    @Test
    public void testReceivedShootMessage() {
        serverGameLogic.addPlayer(1);
        serverGameLogic.addPlayer(2);
        List<Battleship> ships = List.of(new Battleship(3, 2, 2, Rotation.RIGHT));
        MapMessage mapMessage = new MapMessage(ships);
        serverGameLogic.received(mapMessage, 1);
        serverGameLogic.received(mapMessage, 2);

        ShootMessage shootMessage = new ShootMessage(p(2, 2));
        serverGameLogic.received(shootMessage, 1);
        verify(mockServerSender, times(2)).send(anyInt(), any(EffectMessage.class));
    }

    @Test
    public void testPlayerReady() {
        final Player player1 = serverGameLogic.addPlayer(1);
        final Player player2 = serverGameLogic.addPlayer(2);
        List<Battleship> ships = List.of(new Battleship(3, 2, 2, Rotation.RIGHT));
        serverGameLogic.playerReady(player1, ships);
        serverGameLogic.playerReady(player2, ships);
        verify(mockServerSender, times(2)).send(anyInt(), any(StartBattleMessage.class));
        assertEquals(ServerState.BATTLE, serverGameLogic.getState());
    }

    @Test
    public void testShoot() {
        final Player player1 = serverGameLogic.addPlayer(1);
        final Player player2 = serverGameLogic.addPlayer(2);
        List<Battleship> ships = List.of(new Battleship(3, 2, 2, Rotation.RIGHT));
        serverGameLogic.playerReady(player1, ships);
        serverGameLogic.playerReady(player2, ships);

        serverGameLogic.shoot(player1, p(2, 2));
        verify(mockServerSender, times(2)).send(anyInt(), any(EffectMessage.class));
    }

    @Test
    public void testSetState() {
        serverGameLogic.setState(ServerState.BATTLE);
        assertEquals(ServerState.BATTLE, serverGameLogic.getState());
    }

    @Test
    public void testGetOpponent() {
        final Player player1 = serverGameLogic.addPlayer(1);
        final Player player2 = serverGameLogic.addPlayer(2);
        assertEquals(player2, serverGameLogic.getOpponent(player1));
    }

    static IntPoint p(int x, int y) {
        return new IntPoint(x, y);
    }
}
