using API.Controllers;
using API.Models.DataClasses;
using API.Models.Game;
using API.Repository.GameRepo;
using Microsoft.AspNetCore.SignalR;

namespace API.Services.GameManager;

public class GameManager(IGameRepository gameRepository, IHubContext<GameHubSocket> hubContext) : IGameManager
{
    public (string, int) CreateGame(Coordinates gFs, Player player)
    {
        var game = gameRepository.Create(gFs);
        game.AddPlayer(player);
        return (game.Id, game.GameCode);
    }

    public async Task<string?> JoinGame(Player player, int gameCode)
    {
        var game = gameRepository.GetOne(new SixDigitInt(gameCode));
        
        var success = game != null && game.State == GameState.Lobby && game.AddPlayer(player);

        if (!success) return null;

        if (game.Players.Count == 2)
        {
            game.StartGame();

            var gameInfoDto = new GameInformationDto
            {
                Players = game?.Players,
                CurrentField = ConvertField(game?.Field.CurrentField),
                State = game?.State,
                CurrentTurn = game?.CurrentTurn ?? 0
            };

            await hubContext.Clients.Group(game.Id).SendAsync("GameStarted", gameInfoDto);
        }

        return game.Id;
    }

    public async Task RequestGameInformation(string gameId, string playerConnectionId)
    {
        var game = gameRepository.GetOne(gameId);
        if (game == null)
            return;

        var gameInfoDto = new GameInformationDto
        {
            Players = game?.Players,
            CurrentField = ConvertField(game?.Field.CurrentField),
            State = game?.State,
            CurrentTurn = game?.CurrentTurn ?? 0
        };

        await hubContext.Clients.Client(playerConnectionId).SendAsync("GameInformation", gameInfoDto);
    }

    public async Task Drop(string gameCode, int column, string playerConnectionId)
    {
        var game = gameRepository.GetOne(gameCode);

        var player = game?.GetPlayerByConnectionId(playerConnectionId);
        if (player == null)
            return;

        if(game.CurrentTurn != player.PlayerTag) 
            return;

        var result = game.Field.Drop(column, player.PlayerTag);

        if (result != DropResult.Placed)
        {
            await hubContext.Clients.Client(playerConnectionId).SendAsync("Error", "Ungültiger Zug.");
            return;
        }

        game.CurrentTurn = game.CurrentTurn == 1 ? 2 : 1;
        var gameInfoDto = new GameInformationDto
        {
            Players = game?.Players,
            CurrentField = ConvertField(game?.Field.CurrentField),
            State = game?.State,
            CurrentTurn = game?.CurrentTurn ?? 0
        };

        await hubContext.Clients.Group(game.Id).SendAsync("FieldUpdated", gameInfoDto);

        var winResult = game.Field.CheckForWin();
        if (winResult != 0)
        {
            var winPlayer = game.GetPlayerByTag(winResult);
            game.EndGame();
            await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
            {
                Method = "Win",
                Player = winPlayer
            });

            ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));
        }

        if (game.Field.IsFull())
        {
            game.EndGame();

            await hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
            {
                Method = "Draw"
            });

            ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));
        }
    }

    public Task DisconnectedPlayer(string playerConnectionId)
    {
        var game = gameRepository.GetOneByConnectionId(playerConnectionId);
        if (game == null)
            return Task.CompletedTask;

        var player = game.GetPlayerByConnectionId(playerConnectionId);
        if (player == null)
            return Task.CompletedTask;

        game.RemovePlayer(playerConnectionId);
        game.EndGame();
        hubContext.Clients.Group(game.Id).SendAsync("GameEnded", new
        {
            Method = "PlayerDisconnected",
            Player = player
        });

        ScheduleGameDeletion(game.Id, TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    private static int[][] ConvertField(int[,]? field)
    {
        if (field == null)
            return Array.Empty<int[]>();

        var rows = field.GetLength(0);
        var cols = field.GetLength(1);
        var convertedField = new int[rows][];

        for (var y = 0; y < rows; y++)
        {
            convertedField[y] = new int[cols];

            for (var x = 0; x < cols; x++)
            {
                convertedField[y][x] = field[y, x];
            }
        }

        return convertedField;
    }

    private void ScheduleGameDeletion(string gameId, TimeSpan delay)
    {
        _ = Task.Run(async () =>
        {
            await Task.Delay(delay);

            var g = gameRepository.GetOne(gameId);
            if (g != null && g.State == GameState.Ended)
            {
                gameRepository.Destroy(gameId);

                await hubContext.Clients.Group(gameId).SendAsync("GameDestroyed");
            }
        });
    }
}
