package com.nincraft.ninbot.components.channel;

import com.nincraft.ninbot.components.common.Emojis;
import com.nincraft.ninbot.components.config.component.ComponentService;
import com.nincraft.ninbot.components.config.component.ComponentType;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.GuildChannel;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.VoiceChannel;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceJoinEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceLeaveEvent;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.order.ChannelOrderAction;
import net.dv8tion.jda.api.requests.restaction.order.OrderAction;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Log4j2
public class TempVoiceChannelManager extends ListenerAdapter {

    private TempVoiceChannelRepository repository;
    private ComponentService componentService;
    private String componentName;

    public TempVoiceChannelManager(TempVoiceChannelRepository tempVoiceChannelRepository,
            ComponentService componentService) {
        this.repository = tempVoiceChannelRepository;
        this.componentService = componentService;
        this.componentName = "voice-channel-manager";
        componentService.registerComponent(componentName, ComponentType.ACTION);
    }

    @Override
    public void onGuildVoiceJoin(GuildVoiceJoinEvent event) {
        checkIfShouldCreateTempChannel(event.getGuild(), event.getMember(), event.getChannelJoined());
    }

    private void checkIfShouldCreateTempChannel(Guild guild, Member member, VoiceChannel channelJoined) {
        if (componentService.isDisabled(componentName, guild.getId())) {
            return;
        }
        log.trace("hasPermission: {}", hasPermission(guild, Permission.MANAGE_CHANNEL));
        log.trace("channel join {} is temp creator: {}", channelJoined
                .getName(), channelJoined.getName()
                .startsWith(Emojis.PLUS));
        if (hasPermission(guild, Permission.MANAGE_CHANNEL) && channelJoined
                .getName()
                .startsWith(Emojis.PLUS)) {
            createTemporaryChannel(channelJoined, guild, member);
        }
    }

    private void createTemporaryChannel(VoiceChannel channelJoined, Guild guild, Member user) {
        val channelNameType = channelJoined.getName().substring(2);
        val channelName = String.format("%s's %s", user.getEffectiveName(), channelNameType);
        log.info("Creating temporary channel named {} for {} in server {}", channelName, user.getEffectiveName(),
                guild.getId());
        createVoiceChannel(guild, channelJoined, channelName)
                .queue(voiceChannel -> {
                    TempVoiceChannel channel = new TempVoiceChannel(user.getId(), voiceChannel.getId());
                    repository.save(channel);
                    guild.moveVoiceMember(user, voiceChannel).queue();
                    val position = channelJoined.getPosition();
                    modifyVoiceChannelPositions(guild, channelJoined)
                            .selectPosition(voiceChannel)
                            .moveTo(position + 1)
                            .queue();
                    voiceChannel.createPermissionOverride(user)
                            .setAllow(Arrays.asList(Permission.VOICE_MOVE_OTHERS, Permission.PRIORITY_SPEAKER,
                                    Permission.MANAGE_CHANNEL, Permission.VOICE_MUTE_OTHERS,
                                    Permission.VOICE_DEAF_OTHERS))
                            .queue();
                });
    }

    private RestAction<VoiceChannel> createVoiceChannel(Guild guild, VoiceChannel joinedChannel,
            String format) {
        if (joinedChannel.getParent() != null) {
            return joinedChannel.getParent().createVoiceChannel(format);
        } else {
            return guild.createVoiceChannel(format);
        }
    }

    private OrderAction<GuildChannel, ChannelOrderAction> modifyVoiceChannelPositions(
            Guild guild, VoiceChannel joinedChannel) {
        if (joinedChannel.getParent() != null) {
            return joinedChannel.getParent().modifyVoiceChannelPositions();
        } else {
            return guild.modifyVoiceChannelPositions();
        }
    }

    @Override
    public void onGuildVoiceMove(GuildVoiceMoveEvent event) {
        checkIfShouldCreateTempChannel(event.getGuild(), event.getMember(), event.getChannelJoined());
        checkIfShouldDeleteTempChannel(event.getGuild(), event.getChannelLeft());
    }

    @Override
    public void onGuildVoiceLeave(GuildVoiceLeaveEvent event) {
        checkIfShouldDeleteTempChannel(event.getGuild(), event.getChannelLeft());
    }

    private void checkIfShouldDeleteTempChannel(Guild guild, VoiceChannel voiceChannel) {
        if (componentService.isDisabled(componentName, guild.getId())) {
            return;
        }
        if (isTemporaryChannel(voiceChannel.getId()) && hasPermission(guild, Permission.MANAGE_CHANNEL)
                && voiceChannel.getMembers()
                .isEmpty()) {
            deleteTemporaryChannel(voiceChannel);
        }
    }

    private boolean isTemporaryChannel(String voiceChannelId) {
        return repository.findByVoiceChannelId(voiceChannelId).isPresent();
    }

    private void deleteTemporaryChannel(VoiceChannel voiceChannel) {
        voiceChannel.delete()
                .queue(
                        avoid ->
                                repository.findByVoiceChannelId(voiceChannel.getId())
                                        .ifPresent(tempVoiceChannel2 -> repository.delete(tempVoiceChannel2))
                );
    }

    private boolean hasPermission(Guild guild, Permission permission) {
        return guild.getMember(guild.getJDA().getSelfUser()).hasPermission(permission);
    }
}

@@ -1031,16 +1031,54 @@
 .Arrays;
+%0Aimport java.util.concurrent.TimeUnit;
 %0A%0A@Compo
@@ -3275,27 +3275,39 @@
 nnel).queue(
-);%0A
+aVoid -%3E %7B%0A    
             
@@ -3378,16 +3378,20 @@
         
+    
 modifyVo
@@ -3460,16 +3460,20 @@
         
+   
+ 
 .selectP
@@ -3494,16 +3494,20 @@
 hannel)%0A
+    
         
@@ -3580,37 +3580,28 @@
     
+    
 .queue(
-);%0A                   
+aVoid1 -%3E
  voi
@@ -3641,16 +3641,28 @@
 e(user)%0A
+            
         
@@ -3734,16 +3734,64 @@
 _OTHERS,
+%0A                                               
  Permiss
@@ -3812,16 +3812,28 @@
 PEAKER,%0A
+            
         
@@ -3945,32 +3945,44 @@
                 
+    
+        
 Permission.VOICE
@@ -4024,23 +4024,60 @@
         
-.queue(
+            .queue());%0A                    %7D
 );%0A     
@@ -5936,33 +5936,35 @@
 ueue
-(%0A                       
+After(10, TimeUnit.SECONDS,
  avo
@@ -5969,24 +5969,16 @@
 void -%3E%0A
-        
         
@@ -6079,24 +6079,16 @@
         
-     
-   
 .ifPrese
@@ -6148,33 +6148,16 @@
 annel2))
-%0A                
 );%0A    %7D

Fix some performance issues with temp voice channels