using QwirkleClassLibrary.Boards;
using QwirkleClassLibrary.Events;
using QwirkleClassLibrary.Games;
using QwirkleClassLibrary.Players;
using QwirkleClassLibrary.Tiles;
using System.Runtime.CompilerServices;
namespace TestBase;

public class TestGame
{
    [Theory]
    [InlineData(true, false, "test", "test1")]
    [InlineData(false, false, "test", "test")]
    public void Test_GameAddPlayerIngame(bool result, bool gamestate, string p1, string p2)
    {
        Game game = new Game();
        List<string> players = [p1, p2];
        List<string> players2 = ["patrick", "jeanclaude"];

        if (!result)
        {
            game.AddPlayerInGame(players);
            Assert.False(game.AddPlayerInGame(players));
        }
        else
        {
            Assert.True(game.AddPlayerInGame(players));
        }

        if (gamestate)
        {
            game.AddPlayerInGame(players);
            game.StartGame();
            Assert.False(game.AddPlayerInGame(players2));
        }
    }

    [Theory]
    [InlineData(false, null)]
    [InlineData(true, "test")]
    public void Test_GameAddPlayerIngame2(bool result, string? p)
    {
        Game game = new Game();
        List<string> players = new List<string>();

        if (!result)
        {
            players.Add(p!);
            Assert.False(game.AddPlayerInGame(players));
        }

        players.Clear();
        for (int i = 0; i < 4; i++)
        {
            string name = p + i;
            players.Add(name);
        }
        Assert.True(game.AddPlayerInGame(players));
    }

    [Fact]
    public void Test_AddPlayers()
    {
        Game game = new Game();
        List<string> playerstest = new List<string> { "test", "test1" };
        Assert.True(game.AddPlayerInGame(playerstest));
    }


    [Theory]
    [InlineData("test")]
    public void Test_GameCreatePlayers(string p)
    {
        Game game = new Game();
        Player player = new Player(p);
        Assert.Equal(game.CreatePlayer(p).NameTag, player.NameTag);
    }

    [Theory]
    [InlineData(true, "test1", "test2")]
    [InlineData(false, "test1", "test2")]
    public void Test_GameState(bool result, string p1, string p2)
    {
        Game game = new Game();

        if (!result)
        {
            game.StartGame();
            Assert.False(game.GameRunning);
        }
        List<string> playerstest = [p1, p2];
        game.AddPlayerInGame(playerstest);
        game.StartGame();

        Assert.True(game.GameRunning);
    }


    [Theory]
    [InlineData(true, "test1", "test2", "test3")]
    [InlineData(false, "test1", "test2", "test3")]
    public void Test_GameGetPlayingPlayerPosition(bool result, string p1, string p2, string p3)
    {
        Game game = new Game();
        List<string> playerstest = [p1, p2, p3];
        game.AddPlayerInGame(playerstest);
        int i = 0;

        if (!result)
        {
            Assert.Equal(-1, game.GetPlayingPlayerPosition());
            return;
        }
        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);
        
        for(int j = 0; j < game.PlayerList.Count; j++)
        {
            if (game.PlayerList[j] == game.GetPlayingPlayer())
            {
                i = j;
                break;
            }
        }
        
        Assert.Equal(i, game.GetPlayingPlayerPosition());
    }

    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void Test_GameGetPlaylingPlayer(bool result)
    {
        Game game = new Game();
        Player? p = null;
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);
        if (!result)
        {
            Assert.Throws<ArgumentException>(() => game.GetPlayingPlayer());
        }
        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);

        foreach (var pl in game.PlayerList)
        {
            if (pl == game.GetPlayingPlayer())
            {
                p = pl;
                break;
            }
        }
        
        Assert.Equal(p, game.GetPlayingPlayer());

    }

    [Fact]
    public void Test_BoardReturn()
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);
        Assert.IsType<Board>(game.GetBoard());
    }

    [Fact]
    public void Test_EmptyCell()
    {
        Game game = new Game();


        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);

        Board? b = game.GetBoard();
        Cell? c = b!.GetCell(1, 1);

        game.AddCellUsed(c);

        game.EmptyCellUsed();

        Assert.Empty(game.CellsUsed);
    }

    [Fact]
    public void Test_TileOfPlayer()
    {
        Game game = new Game();
        Player? p = null;
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);

        foreach (var pl in game.PlayerList)
        {
            if (pl == game.GetPlayingPlayer())
            {
                p = pl;
                break;
            }
        }
        
        Assert.Equal(p!.Tiles[0], game.TileOfPlayerWithPos(0));
    }

    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void Test_SetFirstPlayer(bool except)
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        if (except)
        {
            game.StartGame();
            game.GiveTilesToPlayers();
            Assert.IsType<string>(game.SetFirstPlayer(game.PlayerList));
            return;
        }

        Assert.Throws<ArgumentException>(() => game.SetFirstPlayer(game.PlayerList));

    }

    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void Test_Setnextplayer(bool except)
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        if (except)
        {
            game.StartGame();
            game.GiveTilesToPlayers();
            game.SetNextPlayer();
            Assert.IsType<string>(game.SetNextPlayer());
            return;
        }

        game.StartGame();
        game.GiveTilesToPlayers();
        Assert.Equal(game.SetNextPlayer(), game.SetFirstPlayer(game.PlayerList));
        return;

    }

    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void Test_PlaceTile(bool except)
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        if (except)
        {
            game.StartGame();
            game.GiveTilesToPlayers();
            game.SetNextPlayer();
            Assert.True(game.PlaceTile(game.GetPlayingPlayer(), game.PlayerList[game.GetPlayingPlayerPosition()].Tiles[0], 1, 1));
            return;
        }

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetNextPlayer();
        Assert.False(game.PlaceTile(game.GetPlayingPlayer(), game.PlayerList[game.GetPlayingPlayerPosition()].Tiles[0], -5, 1));
    }

    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void Test_SwapTiles(bool except)
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetNextPlayer();

        List<Tile> list = [];

        if (except)
        {
            game.DrawTiles(game.GetPlayingPlayer());
            list.Add(game.GetPlayingPlayer().Tiles[1]);
            Assert.True(game.SwapTiles(game.GetPlayingPlayer(), list));
            return;
        }
        
        Assert.False(game.SwapTiles(game.GetPlayingPlayer(), list));
    }

    [Fact]
    public void Test_EndOFgameEvent()
    {
        Player p = new Player("test");

        EndOfGameNotifiedEventArgs events = new EndOfGameNotifiedEventArgs(p);

        Assert.Equal(p, events.Player);
    }

    [Fact]
    public void Test_IsTilePlacementCorrect()
    {
        var game = new Game();
        var board = new Board(17, 14);
        var tile = new Tile(Shape.Club, Color.Red);

        board.AddTileInCell(0, 1, new Tile(Shape.Club, Color.Blue));
        
        var x = 1;
        var y = 1;

        var surroundingCells = new List<Cell?>
        {
            board.GetCell(x + 1, y),
            board.GetCell(x - 1, y),
            board.GetCell(x, y + 1),
            board.GetCell(x, y - 1)
        };

        bool result = game.IsTilePlacementCorrect(tile, x, y, board, surroundingCells);

        Assert.True(result);
    }

    [Fact]
    public void Test_IsMoveCorrect()
    {
        var game = new Game();
        var board = new Board(17, 14);
        var tile = new Tile(Shape.Club, Color.Red);

        board.AddTileInCell(0, 1, new Tile(Shape.Club, Color.Blue));

        var x = 2;
        var y = 1;

        bool result = game.IsMoveCorrect(tile, x, y, board);

        Assert.False(result);
    }

    [Fact]
    public void Test_IsMoveCorrectSixLine()
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetNextPlayer();

        Tile t1 = new Tile(Shape.Square, Color.Red);
        Tile t2 = new Tile(Shape.Club, Color.Red);
        Tile t3 = new Tile(Shape.Round, Color.Red);
        Tile t4 = new Tile(Shape.Shuriken, Color.Red);
        Tile t5 = new Tile(Shape.Star, Color.Red);
        Tile t6 = new Tile(Shape.Rhombus, Color.Red);
        Tile t7 = new Tile(Shape.Round, Color.Red);

        game.GetPlayingPlayer().Tiles.Add(t1);
        game.GetPlayingPlayer().Tiles.Add(t2);
        game.GetPlayingPlayer().Tiles.Add(t3);
        game.GetPlayingPlayer().Tiles.Add(t4);
        game.GetPlayingPlayer().Tiles.Add(t5);
        game.GetPlayingPlayer().Tiles.Add(t6);

        game.PlaceTile(game.GetPlayingPlayer(), t1, 0, 0);
        game.PlaceTile(game.GetPlayingPlayer(), t2, 1, 0);
        game.PlaceTile(game.GetPlayingPlayer(), t3, 2, 0);
        game.PlaceTile(game.GetPlayingPlayer(), t4, 3, 0);
        game.PlaceTile(game.GetPlayingPlayer(), t5, 4, 0);
        game.PlaceTile(game.GetPlayingPlayer(), t6, 5, 0);

        game.GetPlayingPlayer().Tiles.Add(t7);
        
        Assert.False(game.IsMoveCorrect(t7, 6, 0, game.GetBoard()!));

    }

    [Fact]
    public void Test_CheckTileInCompletedLines()
    {
        int nbTiles = 0;
        var checkdoubles = new List<Tile>()
        {
            new(Shape.Club, Color.Blue),
            new(Shape.Club, Color.Red),
            new(Shape.Club, Color.Green),
        };
        
        var t1 = new Tile(Shape.Club, Color.Green);
        
        Assert.False(Game.CheckTileInCompletedLines(t1, ref nbTiles, ref checkdoubles));
    }

    [Fact]
    public void Test_CheckWrongCompletedLines()
    {
        var board = new Board(17, 14);
        var checkDoubles = new List<Tile>();
        int x = 4, y = 1, dx = 1, dy = 0;

        board.AddTileInCell(1, 1, new Tile(Shape.Club, Color.Red));
        board.AddTileInCell(2, 1, new Tile(Shape.Square, Color.Red));
        board.AddTileInCell(3, 1, new Tile(Shape.Star, Color.Red));
        board.AddTileInCell(5, 1, new Tile(Shape.Round, Color.Red));
        board.AddTileInCell(6, 1, new Tile(Shape.Shuriken, Color.Red));
        board.AddTileInCell(7, 1, new Tile(Shape.Rhombus, Color.Red));
        
        bool result = Game.CheckWrongCompletedLines(x, y, dx, dy, board, ref checkDoubles);

        Assert.False(result);
    }

    [Theory]
    [InlineData(3, 1, 4, 1, 5, 1, 5)]
    [InlineData(2, 2, 3, 2, 4, 2, 5)]
    [InlineData(3, 1, 3, 2, 3, 3, 6)]
    public void GetPlayerScore_ReturnsCorrectScore(int x1, int y1, int x2, int y2, int x3, int y3, int expectedScore)
    {
        var game = new Game();
        var player = new Player("TestPlayer");
        var board = game.GetBoard();

        board.AddTileInCell(1, 1, new Tile(Shape.Club, Color.Red));
        board.AddTileInCell(2, 1, new Tile(Shape.Square, Color.Red));

        var c1 = new Cell(x1, y1);
        var c2 = new Cell(x2, y2);
        var c3 = new Cell(x3, y3);

        c1.SetTile(new Tile(Shape.Club, Color.Red));
        c2.SetTile(new Tile(Shape.Square, Color.Red));
        c3.SetTile(new Tile(Shape.Star, Color.Red));

        var cellsPlayed = new List<Cell>
        {
            c1,
            c2,
            c3
        }.AsReadOnly();

        var score = game.GetPlayerScore(player, cellsPlayed, board);

        Assert.Equal(expectedScore, score);
    }

    [Fact]
    public void Test_EndOfGame()
    {
        Game game = new Game();

        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);

        game.CheckGameOver(game.GetPlayingPlayer());

    }

    [Fact]
    public void Test_ClearGame()
    {
        Game game = new Game();
        List<string> playerstest = ["test", "test1"];
        game.AddPlayerInGame(playerstest);

        game.StartGame();
        game.GiveTilesToPlayers();
        game.SetFirstPlayer(game.PlayerList);

        game.ClearGame();

        Assert.Empty(game.PlayerList);
        Assert.Null(game.GetTileBag());
        Assert.Equal(-1, game.GetPlayingPlayerPosition());
    }

}


