package com.nincraft.ninbot.components.twitch;

import com.nincraft.ninbot.components.common.LocaleService;
import com.nincraft.ninbot.components.config.ConfigConstants;
import com.nincraft.ninbot.components.config.ConfigService;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.user.UserActivityEndEvent;
import net.dv8tion.jda.api.events.user.UserActivityStartEvent;
import net.dv8tion.jda.api.events.user.update.GenericUserPresenceEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Component
@Log4j2
public class TwitchListener extends ListenerAdapter {

    private ConfigService configService;
    private Set<SlimMember> streamingMembers;
    private LocaleService localeService;
    private List<SlimMember> cooldownList;

    public TwitchListener(ConfigService configService, LocaleService localeService) {
        this.configService = configService;
        this.localeService = localeService;
        this.streamingMembers = new HashSet<>();
        this.cooldownList = new ArrayList<>();
    }

    @Override
    public void onGenericUserPresence(GenericUserPresenceEvent event) {
        SlimMember member = new SlimMember(event.getMember().getId(), event.getGuild().getId());
        if (event instanceof UserActivityStartEvent) {
            if (!streamingMembers.contains(member)) {
                val activityStartEvent = (UserActivityStartEvent) event;
                val streamingAnnounceUser = configService.getValuesByName(activityStartEvent.getGuild().getId(), ConfigConstants.STREAMING_ANNOUNCE_USERS);
                if (!cooldownList.contains(member) && streamingAnnounceUser.contains(member.getUserId())) {
                    Timer timer = new Timer();
                    announceStream(activityStartEvent);
                    streamingMembers.add(member);
                    timer.schedule(new TwitchAnnounceCooldown(member), Date.from(Instant.now().plus(30, ChronoUnit.MINUTES)));
                }
            }
        } else if (event instanceof UserActivityEndEvent && streamingMembers.contains(member)
                && event.getMember().getActivities().isEmpty()) {
            removeRole(event.getGuild(), event.getMember());
            streamingMembers.remove(member);
            cooldownList.remove(member);
        }
    }

    private void removeRole(Guild guild, Member member) {
        val streamingRoleId = configService.getSingleValueByName(guild.getId(), ConfigConstants.STREAMING_ROLE);
        streamingRoleId.ifPresent(roleId -> {
            val streamingRole = guild.getRoleById(roleId);
            guild.removeRoleFromMember(member, streamingRole).queue();
        });
    }


    private void announceStream(UserActivityStartEvent userActivityStartEvent) {
        val serverId = userActivityStartEvent.getGuild().getId();
        val streamingAnnounceChannel = configService.getSingleValueByName(serverId, ConfigConstants.STREAMING_ANNOUNCE_CHANNEL);
        streamingAnnounceChannel.ifPresent(streamingAnnounceChannelString -> {
            val guild = userActivityStartEvent.getGuild();
            val channel = guild.getTextChannelById(streamingAnnounceChannelString);
            val user = userActivityStartEvent.getUser().getName();
            val url = userActivityStartEvent.getNewActivity().getUrl();
            val gameName = userActivityStartEvent.getNewActivity().getName();
            addRole(guild, guild.getMember(userActivityStartEvent.getUser()));
            ResourceBundle resourceBundle = ResourceBundle.getBundle("lang", localeService.getLocale(serverId));
            channel.sendMessage(String.format(resourceBundle.getString("listener.twitch.announce"), user, gameName, url)).queue();
        });
    }

    private void addRole(Guild guild, Member member) {
        val streamingRoleId = configService.getSingleValueByName(guild.getId(), ConfigConstants.STREAMING_ROLE);
        streamingRoleId.ifPresent(roleId -> {
            val streamingRole = guild.getRoleById(roleId);
            guild.addRoleToMember(member, streamingRole).queue();
        });
    }

    @Data
    class SlimMember {
        String userId;
        String guildId;

        SlimMember(String userId, String guildId) {
            this.userId = userId;
            this.guildId = guildId;
        }
    }

    class TwitchAnnounceCooldown extends TimerTask {

        private SlimMember slimMember;

        TwitchAnnounceCooldown(SlimMember slimMember) {
            this.slimMember = slimMember;
        }

        @Override
        public void run() {
            cooldownList.remove(slimMember);
        }
    }
}

@@ -297,16 +297,62 @@
 ok.val;%0A
+import net.dv8tion.jda.api.entities.Activity;%0A
 import n
@@ -1794,32 +1794,57 @@
 Event.getGuild()
+%0A                        
 .getId(), Config
@@ -1880,16 +1880,201 @@
 USERS);%0A
+                val isStreaming = ((UserActivityStartEvent) event).getNewActivity()%0A                        .getType()%0A                        .equals(Activity.ActivityType.STREAMING);%0A
         
@@ -2169,16 +2169,55 @@
 serId())
+%0A                        && isStreaming
 ) %7B%0A    
@@ -2459,16 +2459,45 @@
 nt.now()
+%0A                            
 .plus(30
@@ -3145,32 +3145,77 @@
 leById(roleId);%0A
+            if (streamingRole != null) %7B%0A    
             guil
@@ -3261,32 +3261,46 @@
 gRole).queue();%0A
+            %7D%0A
         %7D);%0A    
@@ -4010,32 +4010,67 @@
 ty().getName();%0A
+            if (url != null) %7B%0A    
             addR
@@ -4144,16 +4144,20 @@
         
+  
+  
 Resource
@@ -4245,24 +4245,71 @@
 serverId));%0A
+                if (channel != null) %7B%0A        
             
@@ -4417,25 +4417,86 @@
 e, url))
-.queue();
+%0A                            .queue();%0A                %7D%0A            %7D
 %0A       
@@ -4772,32 +4772,77 @@
 leById(roleId);%0A
+            if (streamingRole != null) %7B%0A    
             guil
@@ -4883,32 +4883,46 @@
 gRole).queue();%0A
+            %7D%0A
         %7D);%0A    

Fix streaming announcing any game change