/*
 *   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.commands;

import com.velocitypowered.api.proxy.Player;
import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.Message;
import de.steamwar.persistent.Storage;
import de.steamwar.sql.*;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.mods.ModUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.kyori.adventure.text.event.ClickEvent;

import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Linked
public class WhoisCommand extends SWCommand {
    public WhoisCommand() {
        super("whois");
    }

    @Register(description = "WHOIS_USAGE")
    public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) {
        if (!sender.user().hasPerm(UserPerm.ADMINISTRATION) && !sender.user().hasPerm(UserPerm.PREFIX_DEVELOPER)) {
            sender.system("UNKNOWN_PLAYER");
            return;
        }

        // SW ID or Discord ID
        whois(sender, id < Integer.MAX_VALUE ? SteamwarUser.byId((int) id) : SteamwarUser.get(id), parameters);
    }

    @Register(description = "WHOIS_USAGE")
    public void whois(Chatter sender, SteamwarUser user, WhoisParameterTypes... parameters) {
        EnumSet<WhoisParameterTypes> parameterTypes = parameters.length == 0 ? EnumSet.noneOf(WhoisParameterTypes.class) : EnumSet.copyOf(Arrays.asList(parameters));

        Team team = Team.byId(user.getTeam());

        sender.system("WHOIS_USERNAME", user.getUserName());
        sender.system("WHOIS_PREFIX", user.prefix().getColorCode() + user.prefix().getChatPrefix());
        sender.system("WHOIS_TEAM", new Message("WHOIS_TEAM_HOVER", team.getTeamName()), ClickEvent.runCommand("/team info " + team.getTeamName()), team.getTeamColor(), team.getTeamKuerzel(), team.getTeamName());

        if (!sender.user().hasPerm(UserPerm.TEAM)) return;

        sender.system("WHOIS_STREAMING", !StreamingCommand.isNotStreaming(user));
        if (sender.user().hasPerm(UserPerm.MODERATION) || sender.user().hasPerm(UserPerm.PREFIX_DEVELOPER)) {
            sender.system("WHOIS_ID", user.getId());
            sender.system("WHOIS_UUID", new Message("WHOIS_UUID_HOVER"), ClickEvent.copyToClipboard(user.getUUID().toString()), user.getUUID().toString());
            if (user.getDiscordId() != null) {
                sender.system("WHOIS_DISCORD_ID", user.getDiscordId());
            }

            sender.system("WHOIS_PERMS", user.perms().stream().map(Enum::name).collect(Collectors.joining(", ")));
        }

        Player target = VelocityCore.getProxy().getPlayer(user.getUUID()).orElse(null);
        Timestamp firstJoin = user.getFirstjoin();
        double onlineTime = user.getOnlinetime();
        if (firstJoin == null && target != null) {
            firstJoin = Storage.sessions.get(target);
        }
        Timestamp lastOnline = user.getLastOnline();

        if (firstJoin != null) {
            sender.system("WHOIS_JOINED_FIRST", firstJoin.toString());
        }
        if (lastOnline != null) {
            sender.system("WHOIS_LAST_ONLINE", lastOnline.toString());
        }
        sender.system("WHOIS_HOURS_PLAYED", new DecimalFormat("###.##").format(onlineTime / 3600d));

        if (target != null) {
            sender.system("WHOIS_CURRENT_PLAYED", new DecimalFormat("####.##").format((Instant.now().getEpochSecond() - Storage.sessions.get(target).toInstant().getEpochSecond()) / 60d));
            sender.system("WHOIS_CURRENT_SERVER", target.getCurrentServer().orElseThrow().getServerInfo().getName());
            sender.system("WHOIS_CURRENT_PROTOCOL", target.getProtocolVersion().getMostRecentSupportedVersion());

            List<Mod> mods = ModUtils.getPlayerModMap().get(user.getUUID());
            if (mods == null) {
                mods = Collections.emptyList();
            }

            sender.system("WHOIS_PLATFORM", mods.isEmpty() ? "Vanilla" : mods.get(0).getPlatform().toString());

            if (parameterTypes.contains(WhoisParameterTypes.MOD)) {
                if (!mods.isEmpty()) {
                    sender.system("WHOIS_ACTIVE_MODS", mods.size(), mods.stream().map(mod -> "§" + mod.getModType().getColorCode() + mod.getModName()).collect(Collectors.joining("§8, ")));
                } else {
                    sender.system("WHOIS_NO_ACTIVE_MODS");
                }
            }
        }

        sender.system("WHOIS_PUNISHMENTS");
        List<Punishment> punishmentList = Punishment.getAllPunishmentsOfPlayer(user.getId());
        Set<Punishment.PunishmentType> found = new HashSet<>();
        boolean isPunished = false;
        boolean all = parameterTypes.contains(WhoisParameterTypes.ALL);
        for (Punishment punishment : punishmentList) {
            if (!all && !punishment.getType().isMulti() && !found.add(punishment.getType())) {
                continue;
            }
            if (!all && !punishment.isCurrent()) {
                continue;
            }
            sender.prefixless("WHOIS_PUNISHMENT", SteamwarUser.byId(punishment.getPunisher()), punishment.getType().name(), duration(sender, punishment.getStartTime(), false), duration(sender, punishment.getEndTime(), punishment.isPerma()), punishment.getReason());
            isPunished = true;
        }
        if (!isPunished) {
            sender.system(all ? "WHOIS_NO_ALL_PUNISHMENT" : "WHOIS_NO_PUNISHMENT");
        }
    }

    private Message duration(Chatter sender, Timestamp timestamp, boolean perma) {
        if (perma) {
            return new Message("PUNISHMENT_PERMA");
        } else {
            return new Message("PLAIN_STRING", timestamp.toLocalDateTime().format(DateTimeFormatter.ofPattern(sender.parseToPlain("TIMEFORMAT"))));
        }
    }

    @ClassMapper(value = WhoisParameterTypes.class, local = true)
    public TypeMapper<WhoisParameterTypes> argumentTypeMapper() {
        WhoisParameterTypes[] values = WhoisParameterTypes.values();

        return new TypeMapper<WhoisParameterTypes>() {
            @Override
            public WhoisParameterTypes map(Chatter sender, PreviousArguments previousArguments, String s) {
                SteamwarUser user = sender.user();
                return Stream.of(values)
                        .filter(p -> user.hasPerm(p.perm))
                        .filter(p -> p.getTabCompletes().contains(s))
                        .findFirst()
                        .orElse(null);
            }

            @Override
            public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
                SteamwarUser user = sender.user();
                return Stream.of(values)
                        .filter(p -> user.hasPerm(p.perm))
                        .flatMap(p -> p.getTabCompletes().stream())
                        .collect(Collectors.toList());
            }
        };
    }

    @AllArgsConstructor
    public enum WhoisParameterTypes {
        ALL(Arrays.asList("-a", "-all"), UserPerm.TEAM),
        MOD(Arrays.asList("-m", "-mod", "-mods"), UserPerm.MODERATION);

        @Getter
        private final List<String> tabCompletes;
        private final UserPerm perm;
    }
}
