package com.tterrag.k9.listeners;

import java.time.Duration;
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 lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

@RequiredArgsConstructor
@Slf4j
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(e -> this.tryInvoke(e)
                      .timeout(Duration.ofMinutes(1))
                      .doOnError(t -> log.error("Error dispatching commands:", t))
                      .onErrorResume(t -> e.getMessage().getChannel()
                              .flatMap(c -> c.createMessage(msg -> msg.setContent("Unexpected error occurred dispatching command. Please report this to your bot admin.")))
                              .then()))
              .doOnComplete(() -> log.warn("Command listener completed!"))
              .doOnError(t -> log.error("Command listener errored!", t))
              .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());
    }
}

@@ -77,24 +77,49 @@
 il.HashMap;%0A
+import java.util.Locale;%0A
 import java.
@@ -2021,26 +2021,24 @@
 command%0A
-//
         
 msg.getU
@@ -2033,29 +2033,84 @@
     
-msg.getUserMentions()
+Mono%3CString%3E specialHelpCheck = Mono.just(evt.getMessage())%0A                
 .fil
@@ -2121,130 +2121,134 @@
 hen(
-u
+msg
  -%3E 
-K9.instance.getSelf().map(self -%3E self.getId().equals(u.getId()))).subscribe(user -%3E %7B%0A//            String content =
+msg.getUserMentions().next().map(u -%3E evt.getClient().getSelfId().get().equals(u.getId())))%0A                .map(msg -%3E
  msg
@@ -2291,18 +2291,39 @@
 %22 + 
-user.getId
+evt.getClient().getSelfId().get
 ().a
@@ -2347,20 +2347,22 @@
 ).trim()
-;%0A//
+)%0A    
         
@@ -2369,12 +2369,27 @@
     
-if (
+.filter(content -%3E 
 cont
@@ -2440,13 +2440,9 @@
 *%22))
- %7B%0A//
+%0A
     
@@ -2457,33 +2457,36 @@
     
-CommandRegistrar.INSTANCE
+.flatMap(content -%3E commands
 .inv
@@ -2496,19 +2496,19 @@
 Command(
-msg
+evt
 , %22help%22
@@ -2541,65 +2541,42 @@
 m())
-;%0A//                return;%0A//            %7D%0A//        %7D);
+.thenReturn(%22%22));%0A                
 %0A   
@@ -2756,22 +2756,39 @@
         
-return
+Mono%3C?%3E invokeCommand =
  Mono.ju
@@ -3360,16 +3360,17 @@
       %7D)
+;
 %0A       
@@ -3366,27 +3366,100 @@
 %7D);%0A        
+%0A   
    
+  return specialHelpCheck.switchIfEmpty(invokeCommand.thenReturn(%22%22))
 .then();%0A   

Implement mention help