package com.alttd.webinterface.send_message;

import com.alttd.webinterface.bot.DiscordBotInstance;
import com.alttd.webinterface.objects.MessageForEmbed;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Slf4j
public class DiscordSender {

    private static final DiscordSender INSTANCE = new DiscordSender();

    private final DiscordBotInstance botInstance = DiscordBotInstance.getInstance();

    private DiscordSender() {}

    public static DiscordSender getInstance() {
        return INSTANCE;
    }

    public void sendMessageToChannels(List<Long> channelIds, String message) {
        if (botInstance.getJda() == null) {
            log.error("JDA not initialized; cannot send Discord message.");
            return;
        }
        try {
            if (!botInstance.isReady()) {
                botInstance.getJda().awaitReady();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            log.warn("Error while waiting for JDA ready state", e);
        }

        channelIds.stream()
                .filter(Objects::nonNull)
                .forEach(id -> {
                    TextChannel channel = botInstance.getJda().getChannelById(TextChannel.class, id);
                    if (channel == null) {
                        log.warn("TextChannel with id {} not found", id);
                        return;
                    }
                    channel.sendMessage(message).queue(
                            success -> log.debug("Sent message to channel {}", id),
                            error -> log.error("Failed sending message to channel {}", id, error)
                    );
                });
    }

    public void sendEmbedWithThreadToChannels(List<Long> channelIds, MessageForEmbed messageForEmbed, String threadName) {
        sendEmbedToChannels(channelIds, messageForEmbed).whenCompleteAsync((result, error) -> {
            if (error != null) {
                log.error("Failed sending embed to channels", error);
                return;
            }
            result.stream()
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .forEach(message ->
                                     message.createThreadChannel(threadName).queue());
        });
    }

    public CompletableFuture<List<Optional<Message>>> sendEmbedToChannels(List<Long> channelIds, MessageForEmbed messageForEmbed) {
        List<CompletableFuture<Optional<Message>>> futures = new ArrayList<>();
        for (Long channelId : channelIds) {
            futures.add(sendEmbedToChannel(channelId, messageForEmbed));
        }
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                .thenApply(v ->
                                   futures.stream()
                                           .map(CompletableFuture::join)
                                           .collect(Collectors.toList()));
    }

    public CompletableFuture<Optional<Message>> sendEmbedToChannel(Long channelId, MessageForEmbed messageForEmbed) {
        if (botInstance.getJda() == null) {
            log.error("JDA not initialized; cannot send Discord embed.");
            return CompletableFuture.completedFuture(Optional.empty());
        }
        try {
            if (!botInstance.isReady()) {
                botInstance.getJda().awaitReady();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return CompletableFuture.completedFuture(Optional.empty());
        } catch (Exception e) {
            log.warn("Error while waiting for JDA ready state", e);
            return CompletableFuture.completedFuture(Optional.empty());
        }

        MessageEmbed embed = messageForEmbed.toEmbed();

        TextChannel channel = botInstance.getJda().getChannelById(TextChannel.class, channelId);
        if (channel == null) {
            log.warn("TextChannel with id {} not found when sending embed message", channelId);
            return CompletableFuture.completedFuture(Optional.empty());
        }
        CompletableFuture<Optional<Message>> completableFuture = new CompletableFuture<>();
        channel.sendMessageEmbeds(embed).queue(
                message -> {
                    completableFuture.complete(Optional.of(message));
                    log.debug("Sent embed to channel {}", channelId);
                },
                error -> {
                    completableFuture.complete(Optional.empty());
                    log.error("Failed sending embed to channel {}", channelId, error);
                }
        );
        return completableFuture;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class EmbedField {
        private String name;
        private String value;
        private boolean inline;
    }
}
