using Dpz.Core.Entity.Base.PublicStruct;
using Microsoft.AspNetCore.SignalR;

namespace Dpz.Core.Web.Library.Hub;

/// <summary>
/// 群组聊天客户端接口
/// </summary>
public interface IGroupChatClient
{
    Task OnMessageReceived(GroupChatMessage message);
    Task OnUserJoined(VmUserInfo user);
    Task OnUserLeft(VmUserInfo user);
    Task OnHistoryLoaded(List<GroupChatMessage> messages, bool hasMore);
    Task OnError(string error);
    Task OnDraw(object data);
    Task OnDrawingUserChanged(VmUserInfo? user);
}

/// <summary>
/// 群组聊天Hub
/// </summary>
public class GroupChatHub(
    IChatRecordService chatRecordService,
    ILogger<GroupChatHub> logger,
    IFusionCache fusionCache
) : Hub<IGroupChatClient>
{
    private const string GroupId = WebToolsExtensions.DefaultGroupId;
    private const string DrawingUserKey = "GroupChat:DrawingUser";

    /// <summary>
    /// 加入群组
    /// </summary>
    public async Task<VmUserInfo?> JoinGroup()
    {
        var sessionId = Context
            .GetHttpContext()
            ?.Request.Cookies[Program.AuthorizeCookieName + ".SessionId"];

        if (string.IsNullOrWhiteSpace(sessionId))
        {
            await Clients.Caller.OnError("需要SessionId才能加入群聊");
            return null;
        }

        VmUserInfo user;

        // 检查是否已登录
        if (Context.User?.Authenticated == true)
        {
            user = Context.User.RequiredUserInfo;
        }
        else
        {
            var cacheKey = $"GroupChat:AnonymousUser:{sessionId}";
            user = await fusionCache.GetOrSetAsync(
                cacheKey,
                async _ =>
                {
                    var displayName = await AssignNameAsync();
                    return new VmUserInfo
                    {
                        Id = sessionId,
                        Name = displayName,
                        Avatar =
                            $"https://cravatar.cn/avatar/{sessionId.GenerateHashMd5()}?d=wavatar",
                        Sign = $"匿名用户 - {displayName}",
                        LastAccessTime = DateTime.Now,
                        Key = "",
                    };
                },
                TimeSpan.FromDays(7)
            );
        }

        await Groups.AddToGroupAsync(Context.ConnectionId, GroupId);
        await Clients.Group(GroupId).OnUserJoined(user);

        // 如果当前有人在画图,通知新加入的用户
        var drawingUser = await fusionCache.TryGetAsync<VmUserInfo>(DrawingUserKey);
        if (drawingUser.HasValue)
        {
            await Clients.Caller.OnDrawingUserChanged(drawingUser.Value);
        }

        logger.LogInformation("用户 {UserId} ({UserName}) 加入群组", user.Id, user.Name);

        return user;
    }

    /// <summary>
    /// 发送消息
    /// </summary>
    public async Task SendMessage(string message)
    {
        if (string.IsNullOrWhiteSpace(message))
        {
            await Clients.Caller.OnError("消息内容不能为空");
            return;
        }

        var user = await GetCurrentUserAsync();
        if (user == null)
        {
            return;
        }

        // 保存聊天记录
        await chatRecordService.SaveGroupRecordAsync(user, GroupId, message);

        // 广播消息
        var messageData = new GroupChatMessage
        {
            UserId = user.Id,
            UserName = user.Name,
            Avatar = user.Avatar,
            Message = message,
            SendTime = DateTime.Now,
        };

        await Clients.Group(GroupId).OnMessageReceived(messageData);

        logger.LogInformation(
            "用户 {UserId} ({UserName}) 发送消息: {Message}",
            user.Id,
            user.Name,
            message
        );
    }

    /// <summary>
    /// 请求画图权限
    /// </summary>
    public async Task RequestDrawingAccess()
    {
        var user = await GetCurrentUserAsync();
        if (user == null)
        {
            return;
        }

        // 使用 IFusionCache 实现简单的 分布式锁
        var currentDrawingUser = await fusionCache.TryGetAsync<VmUserInfo>(DrawingUserKey);

        if (!currentDrawingUser.HasValue)
        {
            // 30分钟超时自动释放
            await fusionCache.SetAsync(DrawingUserKey, user, TimeSpan.FromMinutes(30));
            await Clients.Group(GroupId).OnDrawingUserChanged(user);
        }
        else if (currentDrawingUser.Value.Id == user.Id)
        {
            // 已经是自己了
            await Clients.Caller.OnDrawingUserChanged(user);
        }
        else
        {
            await Clients.Caller.OnError(
                $"当前用户 {currentDrawingUser.Value.Name} 正在画图,请稍后再试"
            );
        }
    }

    /// <summary>
    /// 释放画图权限
    /// </summary>
    public async Task ReleaseDrawingAccess()
    {
        var user = await GetCurrentUserAsync();
        if (user == null)
        {
            return;
        }

        var currentDrawingUser = await fusionCache.TryGetAsync<VmUserInfo>(DrawingUserKey);

        if (currentDrawingUser.HasValue && currentDrawingUser.Value.Id == user.Id)
        {
            await fusionCache.RemoveAsync(DrawingUserKey);
            await Clients.Group(GroupId).OnDrawingUserChanged(null);
        }
    }

    /// <summary>
    /// 发送画图数据
    /// </summary>
    public async Task SendDrawingData(object data)
    {
        var user = await GetCurrentUserAsync();
        if (user == null)
        {
            return;
        }

        var currentDrawingUser = await fusionCache.TryGetAsync<VmUserInfo>(DrawingUserKey);

        if (currentDrawingUser.HasValue && currentDrawingUser.Value.Id == user.Id)
        {
            await Clients.OthersInGroup(GroupId).OnDraw(data);
        }
    }

    /// <summary>
    /// 获取历史记录
    /// </summary>
    public async Task GetHistory(int pageIndex = 1, int pageSize = 50)
    {
        if (await GetCurrentUserAsync() == null)
        {
            return;
        }

        // 限制最多1000条
        var maxPageSize = 1000;
        var actualPageSize = Math.Min(pageSize, maxPageSize);
        var actualPageIndex = pageIndex;

        var records = await chatRecordService.GetGroupRecordAsync(
            GroupId,
            actualPageIndex,
            actualPageSize
        );

        List<GroupChatMessage> messages = [];
        foreach (var item in records)
        {
            if (item.Sender == null)
            {
                continue;
            }
            var message = new GroupChatMessage
            {
                UserId = item.Sender.Id,
                UserName = item.Sender.Name,
                Avatar = item.Sender.Avatar,
                Message = item.Message,
                SendTime = item.SendTime,
            };
            messages.Add(message);
        }

        // 计算是否还有更多记录
        var hasMore = records.CurrentPageIndex < records.TotalPageCount;

        await Clients.Caller.OnHistoryLoaded(messages, hasMore);
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var user = await GetCurrentUserAsync();
        if (user != null)
        {
            await Clients.Group(GroupId).OnUserLeft(user);
            logger.LogInformation("用户 {UserId} ({UserName}) 离开群组", user.Id, user.Name);

            // 如果是画图用户断开连接,释放画图权限
            var drawingUser = await fusionCache.TryGetAsync<VmUserInfo>(DrawingUserKey);
            if (drawingUser.HasValue && drawingUser.Value.Id == user.Id)
            {
                await fusionCache.RemoveAsync(DrawingUserKey);
                await Clients.Group(GroupId).OnDrawingUserChanged(null);
            }
        }

        await base.OnDisconnectedAsync(exception);
    }

    private async Task<VmUserInfo?> GetCurrentUserAsync()
    {
        var sessionId = Context
            .GetHttpContext()
            ?.Request.Cookies[Program.AuthorizeCookieName + ".SessionId"];

        if (string.IsNullOrWhiteSpace(sessionId))
        {
            await Clients.Caller.OnError("需要SessionId");
            return null;
        }

        if (Context.User?.Authenticated == true)
        {
            return Context.User.RequiredUserInfo;
        }

        var cacheKey = $"GroupChat:AnonymousUser:{sessionId}";
        var anonymousUser = await fusionCache.TryGetAsync<VmUserInfo>(cacheKey);

        if (!anonymousUser.HasValue)
        {
            await Clients.Caller.OnError("用户信息不存在,请重新加入群组");
            return null;
        }
        return anonymousUser.Value;
    }

    private async Task<string> AssignNameAsync()
    {
        for (var i = 0; i < 5; i++)
        {
            var candidate = GenerateBaseName();
            var nameKey = $"GroupChat:TakenName:{candidate}";
            var isTaken = await fusionCache.TryGetAsync<string>(nameKey);
            if (!isTaken.HasValue)
            {
                await fusionCache.SetAsync(nameKey, "taken", TimeSpan.FromDays(7));
                return candidate;
            }
        }

        var baseName = GenerateBaseName();
        var suffix = Random.Shared.Next(1000, 9999);
        var finalName = $"{baseName}{suffix}";

        // 占位
        await fusionCache.SetAsync(
            $"GroupChat:TakenName:{finalName}",
            "taken",
            TimeSpan.FromDays(7)
        );

        return finalName;

        string GenerateBaseName()
        {
            var adjList = Adjectives.AdjectiveList;
            var nameList = AnonymousNames.NameList;

            var adj = adjList[Random.Shared.Next(adjList.Count)];
            var name = nameList[Random.Shared.Next(nameList.Count)];
            return $"{adj}de{name}";
        }
    }
}
评论加载中...