using SharpChat.Channels;
using SharpChat.Configuration;
using SharpChat.Events;
using SharpChat.Snowflake;
using SharpChat.Users;

namespace SharpChat.C2SPacketHandlers;

public class SendMessageC2SPacketHandler(
    RandomSnowflake randomSnowflake,
    CachedValue<int> maxMsgLength
) : C2SPacketHandler {
    private readonly CachedValue<int> MaxMessageLength = maxMsgLength ?? throw new ArgumentNullException(nameof(maxMsgLength));

    private List<ClientCommand> Commands { get; } = [];

    public void AddCommand(ClientCommand command) {
        Commands.Add(command ?? throw new ArgumentNullException(nameof(command)));
    }

    public void AddCommands(IEnumerable<ClientCommand> commands) {
        Commands.AddRange(commands ?? throw new ArgumentNullException(nameof(commands)));
    }

    public bool IsMatch(C2SPacketHandlerContext ctx) {
        return ctx.CheckPacketId("2");
    }

    public async Task Handle(C2SPacketHandlerContext ctx) {
        if(ctx.Session is null)
            return;

        string[] args = ctx.SplitText(3);

        User? user = ctx.Chat.Users.GetUser(ctx.Session.UserId);
        string? messageText = args.ElementAtOrDefault(2);

        if(user?.Permissions.HasFlag(UserPermissions.SendMessage) != true
            || string.IsNullOrWhiteSpace(messageText))
            return;

        // Extra validation step, not necessary at all but enforces proper formatting in SCv1.
        if(!long.TryParse(args[1], out long mUserId) || user.UserId != mUserId.ToString())
            return;

        ctx.Chat.ContextAccess.Wait();
        try {
            Channel? channel = ctx.Chat.ChannelsUsers.GetUserLastChannel(user);
            if(channel is null)
                return;

            ctx.Chat.ChannelsUsers.RecordChannelUserActivity(channel, user);

            if(user.Status != UserStatus.Online)
                await ctx.Chat.UpdateUser(user, status: UserStatus.Online);

            int maxMsgLength = MaxMessageLength;
            messageText = messageText.TruncateIfTooLong(maxMsgLength, maxMsgLength * 10).Trim();

            if(messageText.StartsWith('/')) {
                ClientCommandContext context = new(messageText, ctx.Chat, user, ctx.Session, ctx.Connection, channel);
                foreach(ClientCommand cmd in Commands)
                    if(cmd.IsMatch(context)) {
                        await cmd.Dispatch(context);
                        return;
                    }
            }

            await ctx.Chat.DispatchEvent(new MessageCreateEvent(
                randomSnowflake.Next(),
                channel.Name,
                user.UserId,
                user.UserName,
                user.Colour,
                user.Rank,
                user.NickName,
                user.Permissions,
                DateTimeOffset.Now,
                messageText,
                false, false, false
            ));
        } finally {
            ctx.Chat.ContextAccess.Release();
        }
    }
}