using Microsoft.AspNetCore.SignalR; // Hub
using Northwind.Chat.Models; // UserModel, MessageModel

namespace Northwind.SignalR.Service.Hubs;

public class ChatHub : Hub
{
  // Tworzymy nową instancję klasy ChatHub do przetwarzania każdej metody,
  // więc musimy zapisywać nazwy użytkowników, identyfikatory połączeń
  // i grupy w statycznym polu.
  private static Dictionary<string, UserModel> Users = new();

  public async Task Register(UserModel newUser)
  {
    UserModel user;
    string action = "nowy użytkownik został zarejestrowany";

    // Próba dopasowania nowego użytkownika do zapisanego.
    if (Users.ContainsKey(newUser.Name))
    {
      user = Users[newUser.Name];

      // Usunięcie zarejestrowanych grup.
      if (user.Groups is not null)
      {
        foreach (string group in user.Groups.Split(','))
        {
          await Groups.RemoveFromGroupAsync(user.ConnectionId, group);
        }
      }
      user.Groups = newUser.Groups;

      // Po odświeżeniu strony identyfikator połączenia może się
      // zmienić, dlatego trzeba go zaktualizować.
      user.ConnectionId = Context.ConnectionId;

      action = "zarejestrowany użytkownik został zaktualizowany";
    }
    else
    {
      if (string.IsNullOrEmpty(newUser.Name))
      {
        // Przypisanie anonimowemu użytkownikowi identyfikatora GUID.
        newUser.Name = Guid.NewGuid().ToString();
      }
      newUser.ConnectionId = Context.ConnectionId;
      Users.Add(key: newUser.Name, value: newUser);
      user = newUser;
    }

    if (user.Groups is not null)
    {
      // Użytkownik nie musi należeć do żadnej grupy.
      // Jeżeli należy, rejestrujemy go w koncentratorze.

      foreach (string group in user.Groups.Split(','))
      {
        await Groups.AddToGroupAsync(user.ConnectionId, group);
      }
    }

    // Wysłanie do użytkownika komunikatu o pomyślnej rejestracji.
    MessageModel message = new() 
    { 
      From = "Koncentrator SignalR", To = user.Name, 
      Body = string.Format(
        "{0}. Identyfikator połączenia: {1}.",
        arg0: action, arg1: user.ConnectionId)
    };

    IClientProxy proxy = Clients.Client(user.ConnectionId);
    await proxy.SendAsync("ReceiveMessage", message);
  }

  public async Task SendMessage(MessageModel message)
  {
    IClientProxy proxy;

    if (string.IsNullOrEmpty(message.To))
    {
      message.To = "wszystkich";
      proxy = Clients.All;
      await proxy.SendAsync("ReceiveMessage", message);
      return;
    }

    // Jeżeli właściwość To ma wartość, przekształcamy ją w listę użytkowników i grup.
    string[] userAndGroupList = message.To.Split(',');

    // Każdy element może być nazwą użytkownika lub grupy.
    foreach (string userOrGroup in userAndGroupList)
    {
      if (Users.ContainsKey(userOrGroup))
      {
        // Jeśli element znajduje się w słowniku Users, wyszukujemy 
        // identyfikator jego połączenia i wysyłamy mu komunikat.
        message.To = $"użytkownika {Users[userOrGroup].Name}";
        proxy = Clients.Client(Users[userOrGroup].ConnectionId);
      }
      else // Przyjmujemy, że element jest nazwą grupy użytkowników, do których ma zostać wysłany komunikat.
      {
        message.To = $"grupy {userOrGroup}";
        proxy = Clients.Group(userOrGroup);
      }
      await proxy.SendAsync("ReceiveMessage", message);
    }
  }
}
