package com.tterrag.k9.listeners;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.LongFunction;
import java.util.regex.Pattern;

import com.tterrag.k9.commands.CommandTrick;
import com.tterrag.k9.commands.api.CommandRegistrar;

import discord4j.core.event.EventDispatcher;
import discord4j.core.event.domain.message.MessageCreateEvent;
import discord4j.core.object.util.Snowflake;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
public class CommandListener {
    
    public static final String DEFAULT_PREFIX = "!";
	public static final String CMD_PATTERN = "%s(%s)?(\\S+)(?:\\s(.*))?$";

    public static LongFunction<String> prefixes = id -> DEFAULT_PREFIX;
    private static final Map<String, Pattern> patternCache = new HashMap<>();
    
    private final CommandRegistrar commands;
    
    public void subscribe(EventDispatcher events) {
        events.on(MessageCreateEvent.class)
              .filter(e -> e.getMessage().getAuthor().map(u -> !u.isBot()).orElse(true))
              .filter(e -> e.getMessage().getContent().isPresent())
              .flatMap(this::tryInvoke)
              .subscribe();
    }
    
    private Mono<Void> tryInvoke(MessageCreateEvent evt) {
        // Hardcoded check for "@K9 help" for a global help command
//        msg.getUserMentions().filterWhen(u -> K9.instance.getSelf().map(self -> self.getId().equals(u.getId()))).subscribe(user -> {
//            String content = msg.getContent().get().replaceAll("<@!?" + user.getId().asLong() + ">", "").trim();
//            if (content.toLowerCase(Locale.ROOT).matches("^help.*")) {
//                CommandRegistrar.INSTANCE.invokeCommand(msg, "help", content.substring(4).trim());
//                return;
//            }
//        });
        Snowflake guild = evt.getGuildId().orElse(null);
        String cmdPrefix = getPrefix(guild);
        String trickPrefix = CommandTrick.getTrickPrefix(guild);
        
        return Mono.just(patternCache.computeIfAbsent(cmdPrefix + trickPrefix, prefix -> Pattern.compile(String.format(CMD_PATTERN, Pattern.quote(cmdPrefix), Pattern.quote(trickPrefix)), Pattern.DOTALL)))
           .map(p -> p.matcher(evt.getMessage().getContent().get()))
           .filter(m -> m.matches())
           .flatMap(m -> {
               boolean expand = m.group(1) != null;
               String args = m.group(3);
               return commands.invokeCommand(evt, expand ? "trick" : m.group(2), expand ? m.group(2) + (args == null ? "" : " " + args) : m.group(3));
           })
           .then();
    }
    
    public static String getPrefix(Optional<Snowflake> guild) {
        return getPrefix(guild.orElse(null));
    }
    
    public static String getPrefix(Snowflake guild) {
        return guild == null ? DEFAULT_PREFIX : prefixes.apply(guild.asLong());
    }
}

@@ -467,16 +467,50 @@
 ructor;%0A
+import lombok.extern.slf4j.Slf4j;%0A
 import r
@@ -563,16 +563,23 @@
 tructor%0A
+@Slf4j%0A
 public c
@@ -1217,14 +1217,18 @@
 Map(
+e -%3E 
 this
-::
+.
 tryI
@@ -1232,16 +1232,383 @@
 ryInvoke
+(e)%0A                      .doOnError(t -%3E log.error(%22Error dispatching commands:%22, t))%0A                      .onErrorResume(t -%3E e.getMessage().getChannel()%0A                              .flatMap(c -%3E c.createMessage(msg -%3E msg.setContent(%22Unexpected error occurred dispatching command. Please report this to your bot admin.%22)))%0A                              .then())
 )%0A      

Add top-level resume safeguard on command listener