package su.cif.infernote;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import su.cif.infernote.audio.AloneInVoiceHandler;
import su.cif.infernote.audio.AudioHandler;
import su.cif.infernote.audio.NowplayingHandler;
import su.cif.infernote.audio.PlayerManager;
import su.cif.infernote.gui.GUI;
import su.cif.infernote.playlist.PlaylistLoader;
import su.cif.infernote.settings.SettingsManager;
import java.util.Objects;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;
import java.io.*;
import java.nio.file.*;
import java.util.regex.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Bot
{
    private final EventWaiter waiter;
    private final ScheduledExecutorService threadpool;
    private final BotConfig config;
    private final SettingsManager settings;
    private final PlayerManager players;
    private final PlaylistLoader playlists;
    private final NowplayingHandler nowplaying;
    private final AloneInVoiceHandler aloneInVoiceHandler;
    
    private boolean shuttingDown = false;
    private JDA jda;
    private GUI gui;
    
    public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings)
    {
        this.waiter = waiter;
        this.config = config;
        this.settings = settings;
        this.playlists = new PlaylistLoader(config);
        this.threadpool = Executors.newSingleThreadScheduledExecutor();
        
        //Update config.txt before init
        updateConfig();
        
        this.players = new PlayerManager(this, config);
        this.players.init();
        this.nowplaying = new NowplayingHandler(this);
        this.nowplaying.init();
        this.aloneInVoiceHandler = new AloneInVoiceHandler(this);
        this.aloneInVoiceHandler.init();
    }
    
    public BotConfig getConfig()
    {
        return config;
    }
    
    public SettingsManager getSettingsManager()
    {
        return settings;
    }
    
    public EventWaiter getWaiter()
    {
        return waiter;
    }
    
    public ScheduledExecutorService getThreadpool()
    {
        return threadpool;
    }
    
    public PlayerManager getPlayerManager()
    {
        return players;
    }
    
    public PlaylistLoader getPlaylistLoader()
    {
        return playlists;
    }
    
    public NowplayingHandler getNowplayingHandler()
    {
        return nowplaying;
    }

    public AloneInVoiceHandler getAloneInVoiceHandler()
    {
        return aloneInVoiceHandler;
    }
    
    public JDA getJDA()
    {
        return jda;
    }
    
    public void closeAudioConnection(long guildId)
    {
        Guild guild = jda.getGuildById(guildId);
        if(guild!=null)
            threadpool.submit(() -> guild.getAudioManager().closeAudioConnection());
    }
    
    public void resetGame()
    {
        Activity game = config.getGame()==null || config.getGame().getName().equalsIgnoreCase("none") ? null : config.getGame();
        if(!Objects.equals(jda.getPresence().getActivity(), game))
            jda.getPresence().setActivity(game);
    }

    public void shutdown()
    {
        if(shuttingDown)
            return;
        shuttingDown = true;
        threadpool.shutdownNow();
        if(jda.getStatus()!=JDA.Status.SHUTTING_DOWN)
        {
            jda.getGuilds().stream().forEach(g -> 
            {
                g.getAudioManager().closeAudioConnection();
                AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler();
                if(ah!=null)
                {
                    ah.stopAndClear();
                    ah.getPlayer().destroy();
                }
            });
            jda.shutdown();
        }
        if(gui!=null)
            gui.dispose();
        System.exit(0);
    }

    public void setJDA(JDA jda)
    {
        this.jda = jda;
    }
    
    public void setGUI(GUI gui)
    {
        this.gui = gui;
    }
    
    private void updateConfig()
    {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String currentTime = sdf.format(new Date());

        try {
            Process process = new ProcessBuilder("docker", "run", "quay.io/invidious/youtube-trusted-session-generator")
                    .redirectErrorStream(true)
                    .start();

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
            process.waitFor();

            String dockerOutput = output.toString();
            Pattern poTokenPattern = Pattern.compile("po_token:\\s*([^\\s]+)");
            Pattern visitorDataPattern = Pattern.compile("visitor_data:\\s*([^\\s]+)");

            Matcher poTokenMatcher = poTokenPattern.matcher(dockerOutput);
            Matcher visitorDataMatcher = visitorDataPattern.matcher(dockerOutput);

            if (poTokenMatcher.find() && visitorDataMatcher.find()) {
                String poToken = poTokenMatcher.group(1);
                String visitorData = visitorDataMatcher.group(1);

                Path configPath = Paths.get("config.txt");
                String configContent = Files.readString(configPath);

                if (!configContent.contains("ytpotoken =")) {
                    configContent += "\nytpotoken = \"" + poToken + "\"";
                } else {
                    configContent = configContent.replaceAll("ytpotoken\\s*=\\s*\"[^\"]*\"", "ytpotoken = \"" + poToken + "\"");
                }
                
                if (!configContent.contains("//New PO_TOKEN generated at:")) {
                    configContent += "\n//New PO_TOKEN generated at: " + currentTime;
                } else {
                    configContent = configContent.replaceAll("(?<=ytpotoken\\s*=\\s*\"[^\"]*\")[^/]*(?=//New PO_TOKEN generated at:)", "");
                    configContent = configContent.replaceAll("(?<=ytpotoken\\s*=\\s*\"[^\"]*\")", "//New PO_TOKEN generated at: " + currentTime); 
                }
                
                if (!configContent.contains("ytvisitordata =")) {
                    configContent += "\nytvisitordata = \"" + visitorData + "\"";
                } else {
                    configContent = configContent.replaceAll("ytvisitordata\\s*=\\s*\"[^\"]*\"", "ytvisitordata = \"" + visitorData + "\"");
                }
                
                if (!configContent.contains("//New VISITOR_DATA generated at:")) {
                    configContent += "\n//New VISITOR_DATA generated at: " + currentTime;
                } else {
                    configContent = configContent.replaceAll("(?<=ytvisitordata\\s*=\\s*\"[^\"]*\")[^/]*(?=//New VISITOR_DATA generated at:)", "");
                    configContent = configContent.replaceAll("(?<=ytvisitordata\\s*=\\s*\"[^\"]*\")", "//New VISITOR_DATA generated at: " + currentTime);
                }
                
                Files.writeString(configPath, configContent);

                System.out.println("[" + currentTime + "] [INFO] Config.txt succesfully updated!");
                System.out.println("[" + currentTime + "] [INFO] ytpotoken = " + poToken);
                System.out.println("[" + currentTime + "] [INFO] ytvisitordata = " + visitorData);
            } else {
                System.err.println("[" + currentTime + "] [ERROR]: Failed to find po_token or visitor_data in Docker result.");
            }
        } catch (Exception e) {
            System.err.println("[" + currentTime + "] [ERROR]: Error while updating config.txt " + e.getMessage());
        }
    }
}
