/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.velocitycore.discord;

import de.steamwar.command.SWCommand;
import de.steamwar.messages.Chatter;
import de.steamwar.sql.Event;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.discord.channels.*;
import de.steamwar.velocitycore.discord.listeners.ChannelListener;
import de.steamwar.velocitycore.discord.listeners.DiscordSchemUpload;
import de.steamwar.velocitycore.discord.listeners.DiscordTeamEvent;
import de.steamwar.velocitycore.discord.listeners.DiscordTicketHandler;
import de.steamwar.velocitycore.discord.util.AuthManager;
import lombok.Getter;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

public class DiscordBot {
    public static final String ARGUMENT_NAME = "arguments";

    @Getter
    private static DiscordBot instance;

    @Getter
    private static final Map<String, SWCommand> commands = new HashMap<>();
    private final OptionData commandArgument = new OptionData(OptionType.STRING, ARGUMENT_NAME, "Command arguments", false);

    public static void withBot(Consumer<DiscordBot> consumer) {
        if (instance != null)
            consumer.accept(instance);
    }

    public static Guild getGuild() {
        return instance.jda.getGuildById(instance.config.getGuild());
    }

    @Getter
    private DiscordChannel announcementChannel;
    @Getter
    private DiscordChatRoom ingameChat;
    @Getter
    private DiscordChatRoom serverTeamChat;
    @Getter
    private StaticMessageChannel eventChannel;
    @Getter
    private ChecklistChannel checklistChannel;

    @Getter
    private final DiscordConfig config;
    @Getter
    private final JDA jda;

    public DiscordBot(DiscordConfig config) {
        this.config = config;

        jda = JDABuilder
                .createDefault(config.getToken())
                .setStatus(OnlineStatus.ONLINE)
                .setMemberCachePolicy(MemberCachePolicy.ONLINE)
                .enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.MESSAGE_CONTENT)
                .build();

        instance = this;
        VelocityCore.schedule(this::asyncInit).schedule();
    }

    private void asyncInit() {
        try {
            jda.awaitReady();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        activity();
        new StaticMessageChannel(config.channel("roles"), () -> new MessageCreateBuilder()
                .setContent("**Rollenvergabe**\nKlicke um eine Rolle zu bekommen:")
                .setComponents(ActionRow.of(config.getRoles().values().stream().map(DiscordConfig.DiscordRole::toButton).toArray(Button[]::new))), event -> InteractionReply.reply(event, reply -> {
            Member member = event.getMember();
            Guild guild = event.getGuild();
            Role role = guild.getRoleById(event.getComponentId());

            if (member.getRoles().stream().anyMatch(role::equals)) {
                guild.removeRoleFromMember(member, role).queue();
                reply.system("DC_ROLE_REMOVED", role.getAsMention());
            } else {
                guild.addRoleToMember(member, role).queue();
                reply.system("DC_ROLE_ADDED", role.getAsMention());
            }
        }));

        new StaticMessageChannel(config.channel("rules"), () -> new MessageCreateBuilder()
                .setEmbeds(new EmbedBuilder()
                        .setDescription(String.join("\n", config.getRules()))
                        .setColor(Color.GRAY)
                        .setAuthor("SteamWar", "https://steamwar.de")
                        .setTitle("Regeln und Infos")
                        .build())
                .setComponents(
                        ActionRow.of(Button.link("https://steamwar.de", "Website"), Button.link("https://steamwar.de/youtube", "YouTube")),
                        ActionRow.of(Button.primary("auth", Emoji.fromUnicode("U+2705")).withLabel("Minecraft verknüpfen"))
                ), event -> {
            if (event.getComponentId().equals("auth"))
                event.reply("Gebe innerhalb der nächsten 10 Minuten ``/verify " + AuthManager.createDiscordAuthToken(event.getUser()) + "`` auf dem Minecraft Server ein").setEphemeral(true).queue();
        });

        List<ActionRow> actionRows = new ArrayList<>();
        List<Button> list = new ArrayList<>();
        for (DiscordTicketType type : DiscordTicketType.values()) {
            list.add(type.toButton());
            if (list.size() > 3) {
                actionRows.add(ActionRow.of(list.toArray(Button[]::new)));
                list.clear();
            }
        }
        if (!list.isEmpty()) {
            actionRows.add(ActionRow.of(list.toArray(Button[]::new)));
        }
        new StaticMessageChannel(config.channel("ticket"), () -> new MessageCreateBuilder()
                .setEmbeds(new EmbedBuilder()
                        .setDescription("Hier kannst du Tickets öffnen, welche nur von dir und Teammitgliedern eingesehen werden können.")
                        .setTitle("SteamWar Tickets")
                        .setColor(Color.RED)
                        .build())
                .setComponents(actionRows), DiscordTicketHandler::openTicket);

        eventChannel = new StaticMessageChannel(config.channel("events"), EventChannel::get);
        checklistChannel = new ChecklistChannel(config.channel("checklist"));
        config.getCouncilThread().forEach((roleId, threadId) -> new CouncilChannel(DiscordBot.getGuild().getRoleById(roleId), DiscordBot.getGuild().getThreadChannelById(threadId)));

        announcementChannel = new DiscordChannel(config.channel("announcement"), 0) {
            @Override
            public void received(MessageReceivedEvent event) {
                Chatter.broadcast().system("ALERT", event.getMessage().getContentDisplay());
            }
        };

        // There is a hard limit of 30 messages per minute to send as a webhook, thus with 8 webhooks we can send
        // 240 messages per minute. Which means 4 every second. I looked at the WGS fights and there were around
        // ~70 in a short burst and then rather long no new message.
        ingameChat = new DiscordChatRoom(config.channel("ingame"), "CHAT_DISCORD_GLOBAL", Chatter::globalChat, 8);
        // 60 messages per minute should be enough for the server team!
        serverTeamChat = new DiscordChatRoom(config.channel("serverteam"), "CHAT_DISCORD_SERVERTEAM", Chatter::serverteam, 2);

        VelocityCore.schedule(() -> {
            try {
                activity();
                eventChannel.update();
                checklistChannel.update();
            } catch (ErrorResponseException e) {
                //ignored
            }
        }).repeat(30, TimeUnit.SECONDS).schedule();

        VelocityCore.schedule(CouncilChannel::updateAll).repeat(1, TimeUnit.HOURS).schedule();

        VacationCommand vacationCommand = new VacationCommand();
        jda.addEventListener(
                new DiscordTicketHandler(),
                new DiscordTeamEvent(),
                new ChannelListener(),
                new DiscordSchemUpload(),
                vacationCommand
        );

        jda.updateCommands()
                .addCommands(commands.keySet()
                        .stream()
                        .filter(command -> command.matches("^[\\w-]+$"))
                        .map(command -> Commands.slash(command, command).addOptions(commandArgument))
                        .toArray(CommandData[]::new))
                .queue();

        jda.getGuildById(1241489896909180998L)
                .upsertCommand(vacationCommand.COMMAND)
                .queue();
    }

    private boolean activityToggle = false;

    private void activity() {
        if (activityToggle) {
            Event event = Event.get();
            jda.getPresence().setActivity(event != null ? Activity.competing("dem Event " + event.getEventName()) : Activity.playing("auf SteamWar.de"));
        } else {
            int count = VelocityCore.getProxy().getPlayerCount();
            jda.getPresence().setActivity(Activity.playing(count == 1 ? "mit 1 Spieler" : ("mit " + count + " Spielern")));
        }

        activityToggle = !activityToggle;
    }
}
