using DbController.SqlServer;
using DbController;
using Microsoft.AspNetCore.SignalR;
using Tabletop.Core.Models;
using Tabletop.Core.Services;
using Tabletop.Core.Constants;

namespace Tabletop.Hubs
{
    public class LobbyHub(UserService userService) : Hub
    {
        private readonly UserService _userService = userService;

        public async Task JoinLobby(int userId, int team, string guid)
        {
            if (!AppdataService.Games.TryGetValue(guid, out var game))
            {
                await Clients.Caller.SendAsync("GameClosed");
                return;
            }

            string groupName = $"Game-{guid}";

            var existingPlayer = game.Players.FirstOrDefault(p => p.UserId == userId);

            if (existingPlayer != null)
            {
                if (existingPlayer.ConnectionId != Context.ConnectionId)
                {
                    Console.WriteLine($"Player {userId} is rejoining lobby {guid} with new connection.");
                    existingPlayer.ConnectionId = Context.ConnectionId;

                    await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
                    await Clients.Group(groupName).SendAsync("PlayerUpdated");
                    return;
                }

                await Clients.Caller.SendAsync("PlayerAlreadyInLobby");
                return;
            }

            if (game.Players.Count >= game.NumberOfPlayers)
            {
                await Clients.Caller.SendAsync("LobbyFull");
                return;
            }

            // Neuer Spieler anlegen
            existingPlayer = new Player
            {
                UserId = userId,
                Team = team,
                IsReady = false,
                ConnectionId = Context.ConnectionId
            };

            using IDbController dbController = new SqlController(AppdataService.ConnectionString);
            existingPlayer.User = await _userService.GetUserInformationAsync(userId, dbController) ?? new User();

            existingPlayer.FractionId = existingPlayer.User.MainFractionId == 0
                ? 1
                : existingPlayer.User.MainFractionId;

            var usedColors = game.Players
                .Select(p => p.Color)
                .Where(c => !string.IsNullOrWhiteSpace(c))
                .ToHashSet();

            existingPlayer.Color = PlayerColors.Colors
                .FirstOrDefault(color => !usedColors.Contains(color))
                ?? "#000000";

            game.Players.Add(existingPlayer);

            await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
            await Clients.Group(groupName).SendAsync("PlayerJoined");
        }


        public async Task UpdatePlayer(int userId, int team, int fractionId, bool isReady, string guid)
        {
            if (AppdataService.Games.TryGetValue(guid, out var game))
            {
                var playerToUpdate = game.Players.FirstOrDefault(p => p.UserId == userId);
                if (playerToUpdate != null)
                {
                    playerToUpdate.Team = team;
                    playerToUpdate.FractionId = fractionId;
                    playerToUpdate.IsReady = isReady;

                    string groupName = $"Game-{guid}";
                    await Clients.Group(groupName).SendAsync("PlayerUpdated");
                }
            }
        }

        public async Task UpdateGame(string guid)
        {
            if (AppdataService.Games.TryGetValue(guid, out _))
            {
                string groupName = $"Game-{guid}";
                await Clients.Group(groupName).SendAsync("GameUpdated");
            }
        }

        public async Task GameStartAsync(string guid)
        {
            if (AppdataService.Games.TryGetValue(guid, out _))
            {
                string groupName = $"Game-{guid}";
                await Clients.Group(groupName).SendAsync("GameStarted");
            }
        }

        public override async Task OnDisconnectedAsync(Exception? exception)
        {
            foreach (var game in AppdataService.Games.Values)
            {
                var playerToRemove = game.Players.FirstOrDefault(p => p.ConnectionId == Context.ConnectionId);

                if (playerToRemove != null)
                {
                    if (playerToRemove.UserId == game.UserId)
                    {
                        string groupName = $"Game-{game.GUID}";

                        await Clients.Group(groupName).SendAsync("GameClosed");

                        foreach (var player in game.Players)
                        {
                            await Groups.RemoveFromGroupAsync(player.ConnectionId, groupName);
                        }

                        AppdataService.Games.Remove(game.GUID);
                    }
                    else
                    {
                        game.Players.Remove(playerToRemove);

                        if (game.Players.Count == 0)
                        {
                            AppdataService.Games.Remove(game.GUID);
                        }
                        else
                        {
                            string groupName = $"Game-{game.GUID}";
                            await Clients.Group(groupName).SendAsync("PlayerDisconnected");
                        }

                        await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"Game-{game.GUID}");
                    }

                    break;
                }
            }

            await base.OnDisconnectedAsync(exception);
        }
    }
}
