package com.alttd.altitudeweb.services.discord;

import com.alttd.altitudeweb.database.Databases;
import com.alttd.altitudeweb.database.discord.OutputChannel;
import com.alttd.altitudeweb.database.discord.OutputChannelMapper;
import com.alttd.altitudeweb.database.litebans.HistoryCountMapper;
import com.alttd.altitudeweb.database.litebans.HistoryRecord;
import com.alttd.altitudeweb.database.litebans.HistoryType;
import com.alttd.altitudeweb.database.litebans.UserType;
import com.alttd.altitudeweb.database.web_db.forms.Appeal;
import com.alttd.altitudeweb.setup.Connection;
import com.alttd.webinterface.send_message.DiscordSender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class AppealDiscord {

    private static final String OUTPUT_TYPE = "APPEAL";

    public void sendAppealToDiscord(Appeal appeal, HistoryRecord history) {
        // Fetch channels
        CompletableFuture<List<OutputChannel>> channelsFuture = new CompletableFuture<>();
        Connection.getConnection(Databases.DISCORD).runQuery(sql -> {
            try {
                List<OutputChannel> channels = sql.getMapper(OutputChannelMapper.class)
                        .getChannelsWithOutputType(OUTPUT_TYPE);
                channelsFuture.complete(channels);
            } catch (Exception e) {
                log.error("Failed to load output channels for {}", OUTPUT_TYPE, e);
                channelsFuture.complete(new ArrayList<>());
            }
        });

        CompletableFuture<Integer> bansF = getCountAsync(HistoryType.BAN, appeal.uuid());
        CompletableFuture<Integer> mutesF = getCountAsync(HistoryType.MUTE, appeal.uuid());
        CompletableFuture<Integer> warnsF = getCountAsync(HistoryType.WARN, appeal.uuid());
        CompletableFuture<Integer> kicksF = getCountAsync(HistoryType.KICK, appeal.uuid());

        List<OutputChannel> channels = channelsFuture.join();
        int bans = bansF.join();
        int mutes = mutesF.join();
        int warns = warnsF.join();
        int kicks = kicksF.join();

        if (channels.isEmpty()) {
            log.warn("No Discord output channels found for type {}. Skipping Discord send.", OUTPUT_TYPE);
            return;
        }

        // Build embed
        boolean active = history.getUntil() == null || history.getUntil() <= 0 || history.getUntil() > System.currentTimeMillis();
        String createdAt = formatInstant(appeal.createdAt());

        List<DiscordSender.EmbedField> fields = new ArrayList<>();
        // Group: User
        fields.add(new DiscordSender.EmbedField(
                "User",
                "Username: " + safe(appeal.username()) + "\n" +
                        "UUID: " + safe(String.valueOf(appeal.uuid())) + "\n" +
                        "Email: " + safe(appeal.email()) + "\n" +
                        "Submitted: " + createdAt,
                false
        ));
        // Group: Punishment
        fields.add(new DiscordSender.EmbedField(
                "Punishment",
                "Type: " + safe(String.valueOf(appeal.historyType())) + "\n" +
                        "ID: " + safe(String.valueOf(appeal.historyId())) + "\n" +
                        "Reason: " + safe(history.getReason()) + "\n" +
                        "Active: " + active,
                false
        ));
        // Group: Previous punishments
        fields.add(new DiscordSender.EmbedField(
                "Previous punishments",
                "Bans: " + bans + "\n" +
                        "Mutes: " + mutes + "\n" +
                        "Warnings: " + warns + "\n" +
                        "Kicks: " + kicks,
                true
        ));

        String description = safe(appeal.reason());

        List<Long> channelIds = channels.stream()
                .map(OutputChannel::channel)
                .toList();

        // colorRgb = null (use default), timestamp = appeal.createdAt if available
        Instant timestamp = appeal.createdAt() != null ? appeal.createdAt() : Instant.now();
        DiscordSender.getInstance().sendEmbedToChannels(
                channelIds,
                "New Appeal Submitted",
                description,
                fields,
                null,
                timestamp,
                null
        );
    }

    private CompletableFuture<Integer> getCountAsync(HistoryType type, java.util.UUID uuid) {
        CompletableFuture<Integer> future = new CompletableFuture<>();
        Connection.getConnection(Databases.LITE_BANS).runQuery(sql -> {
            try {
                Integer count = sql.getMapper(HistoryCountMapper.class)
                        .getUuidPunishmentCount(type, UserType.PLAYER, uuid);
                future.complete(count == null ? 0 : count);
            } catch (Exception e) {
                log.error("Failed to load punishment count for {} ({})", type, uuid, e);
                future.complete(0);
            }
        });
        return future;
    }

    private String safe(String s) {
        return s == null ? "unknown" : s;
    }

    private String formatInstant(Instant instant) {
        if (instant == null) return "unknown";
        return instant.atZone(ZoneId.of("UTC"))
                .format(DateTimeFormatter.ofPattern("yyyy MMMM dd hh:mm a '(UTC)'"));
    }
}
