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

import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import de.steamwar.linkage.Linked;
import de.steamwar.messages.Chatter;
import de.steamwar.network.packets.NetworkPacket;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.mods.*;
import de.steamwar.velocitycore.network.ServerMetaInfo;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.kyori.adventure.text.Component;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import java.util.logging.Level;

@Linked
public class PluginMessage extends BasicListener {

    public static void send(Player player, String legacyChannel, String channel, byte[] data) {
        // 1.12 format change
        send(player, player.getProtocolVersion().greaterThan(ProtocolVersion.MINECRAFT_1_12_2) ? channel : legacyChannel, data);
    }

    public static void send(Player player, String channel, byte[] data) {
        //TODO only if player has registered channel
        player.sendPluginMessage(channel.indexOf(':') != -1 ? MinecraftChannelIdentifier.from(channel) : new LegacyChannelIdentifier(channel), data);
    }

    public static byte[] genBufPacket(Consumer<ByteBuf> generator) {
        ByteBuf buf = Unpooled.buffer();
        generator.accept(buf);

        byte[] packet = new byte[buf.readableBytes()];
        buf.readBytes(packet);
        return packet;
    }

    public static byte[] genStreamPacket(StreamConsumer generator) {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(stream);

        try {
            generator.accept(out);
        } catch (IOException e) {
            throw new SecurityException("Could not create PluginMessage packet", e);
        }

        return stream.toByteArray();
    }

    public interface StreamConsumer {
        void accept(DataOutputStream out) throws IOException;
    }

    private static final Parser UNKNOWN = event -> VelocityCore.getLogger().log(Level.WARNING, () -> "Undefined PluginMessage on channel " + event.getIdentifier() + " from " + event.getSource() + " received.\n" + new String(event.getData()) + "\n" + Arrays.toString(event.getData()));
    private static final Parser PASS_THROUGH = event -> event.setResult(PluginMessageEvent.ForwardResult.forward());
    private static final Parser DROP = event -> {
    };

    private final Lunar lunar = new Lunar();
    private final Badlion badlion = new Badlion();

    private final Set<String> knownBrands = new HashSet<>();
    private final Map<String, Consumer<Player>> channelRegisterHandlers = new HashMap<>();
    private final Map<String, Parser> handlers = new HashMap<>();

    public PluginMessage() {
        LabyMod labyMod = new LabyMod();
        WorldDownloader wdl = new WorldDownloader();

        knownBrands.addAll(Arrays.asList("vanilla", "badlion", "fabric", "quilt", "forge", "optifine", "Geyser", "labymod", "Feather Fabric"));

        for (String channel : Arrays.asList(
                "fabric:container/open", "fabric:registry/sync/direct", "fabric:registry/sync",
                "fabric-screen-handler-api-v1:open_screen", "fabric:attachment_sync_v1",

                FML.CHANNEL, "FML|MP", "FML", "FORGE",
                "fml:loginwrapper", "fml:handshake", "fml:play",
                "forge:tier_sorting", "forge:split", "forge:login", "forge:handshake",

                "labymod3:main", "labymod:neo", "labymod:voicechat", "labymod:germanminer",
                "feather:client/frag",
                Alpine.HANDSHAKE, Alpine.PLAY,
                "lunarclient:pm", "transfer:channel", "apollo:json", //https://lunarclient.dev/apollo/developers/modules/transfer (Lunarclient channels)

                "sw:hotkeys",
                "floodgate:form", "floodgate:w", "floodgate:packet", "floodgate:transfer", "erosion:msg",

                "Replay|Restrict", "replaymod:restrict",
                "WDL|CONTROL", "wdl:control",
                "tpshud:handshake", "tpshud:tps", //https://github.com/mooziii/tpshud-fabric/tree/main
                "methane_server:statepacket", //https://modrinth.com/mod/methane
                "architectury:spawn_entity_packet", //https://modrinth.com/mod/architectury-api
                "jei:channel", "jei:cheat_permission", //https://modrinth.com/mod/jei
                "owo:local_packet", "owo:sync_screen_handler_properties", //https://modrinth.com/mod/owo-lib
                "minecraft:intave", //https://intave.ac seems to be a client side integration of intave with labymod 4
                "midnightcontrols:feature", "midnightcontrols:controls_mode", //https://modrinth.com/mod/midnightcontrols
                "controlify:vibrate_from_origin", "controlify:vibration", "controlify:vibrate_from_entity",
                "carpet:structures", //https://modrinth.com/mod/carpet
                "plasmo:voice/v2/installed", "plasmo:voice/v2", //https://modrinth.com/plugin/plasmo-voice (Voice chat)
                "inventorysorter:sync_blacklist_packet", //https://github.com/cpw/inventorysorter (needs server side component to work)
                "emi:ping", "emi:command", "emi:chess", //https://github.com/emilyploszaj/emi/
                "emi_loot:archaeology", "emi_loot:block", "emi_loot:chest", "emi_loot:clear_loot", "emi_loot:gameplay", "emi_loot:mob", // https://github.com/emilyploszaj/emi
                "fancymenu:execute_command", "fancymenu:packet_bridge", "minecraft:fancymenu_packet_bridge", //https://github.com/Keksuccino/FancyMenu (Custom menus)
                "itemswapper:enableshulker", "itemswapper:enablerefill", //https://github.com/tr7zw/ItemSwapper/tree/main (Easier inventory item swapping)
                "jade:show_overlay", "jade:receive_data", "jade:server_ping", "jade:server_ping_v1", //https://github.com/Snownee/Jade (Information over block/entity under crosshair)
                "bclib:hello_client", "bclib:request_files", "bclib:send_files", "bclib:chunker", //https://github.com/quiqueck/BCLib (Library for additional dimensions)
                "essentialclient:chunkdebug", "essentialclient:clientscript", "essentialclient:gamerule", //https://github.com/senseiwells/EssentialClient (Carpet mod extension)
                "couplings:server_config", //https://github.com/ChloeDawn/Couplings (Opens/closes double doors/gates simultaneously)
                "yigd:grave_overview_s2c", "yigd:grave_selection_s2c", "yigd:player_selection_s2c", //https://github.com/B1n-ry/Youre-in-grave-danger (Adds new block - graves)
                "bagofholding:1/0", //https://github.com/Fuzss/bagofholding (Adds new item - bag)
                "betterend:ritual_update", //https://github.com/quiqueck/BetterEnd (Dimension improvement)
                "calio:sync_data_object_registry", //https://github.com/apace100/calio (Data serialization library)
                "chunk_debug:batched_chunk_info", "chunk_debug:hello", //https://github.com/senseiwells/ChunkDebug (Serverside extension for EssentialClient)
                "chunky:border", //https://github.com/pop4959/Chunky (Serverside chunk generator)
                "commandblockide:edit_function", "commandblockide:update_function_command", //https://github.com/arm32x/command-block-ide (Command block interface)
                "configured:session_data", //https://github.com/MrCrayfish/Configured (Confiuration system)
                "corpse:default", //https://github.com/henkelmax/corpse/tree/master (Adds new entity - corpse)
                "craftingtweaks:hello", "craftingtweaks:sync_config", //https://github.com/TwelveIterationMods/CraftingTweaks (Additional Crafting UI)
                "create:main", //https://github.com/Creators-of-Create/Create (Additional redstone blocks mod)
                "dummmmmmy:0", "dummmmmmy:1", "dummmmmmy:2", //https://github.com/MehVahdJukaar/DuMmmMmmy (Adding dummy target item)
                "easyanvils:1/0", "easyanvils:1/2", "easyanvils:play", //https://github.com/Fuzss/easyanvils (Anvil handling improvements)
                "easymagic:1/0", "easymagic.main:0", "easymagic:play", //https://github.com/Fuzss/easymagic (Enchantment table handling improvements)
                "enhancedvisuals:main0", "enhancedvisuals:main1", "enhancedvisuals:main2", //https://github.com/CreativeMD/EnhancedVisuals (Visual effects)
                "fallingtree:configuration-packet", //https://github.com/RakambdaOrg/FallingTree (Serverside tree cutting enhancements)
                "forgeconfigscreens:play/0", "forgeconfigscreens:play/1", //https://github.com/Fuzss/forgeconfigscreens (Config GUI)
                "fwaystones:void_totem_revive", "fwaystones:sync_player", "fwaystones:waystone_packet", //https://github.com/LordDeatHunter/FabricWaystones (Adds new block - waystone)
                "graveyard:spawn_entity", //https://github.com/finallion/The-Graveyard-FORGE (Adding graveyard themed blocks, items and mobs)
                "immersive_weathering:0", //https://github.com/AstralOrdana/Immersive-Weathering (Adds additional random block transitions)
                "kiwi:sync_cosmetic", //github.com/Snownee/Kiwi (General purpose library)
                "libgui:screen_messag", "libgui:screen_message", "libgui:screen_message_s2c", //https://github.com/cottonmc/libgui (Ingame GUI library)
                "libjf-config-network", "libjf-config-network-v0:request", "libjf-config-network-v0:response", //https://git.frohnmeyer-wds.de/JfMods/LibJF (General purpose library)
                "libz:set_mouse_position", "libz:sync_config", //https://github.com/Globox1997/LibZ (General purpose library)
                "moonlight:0", "moonlight:1", "moonlight:2", "moonlight:3", "moonlight:4", "moonlight:5", //https://github.com/MehVahdJukaar/Moonlight (General purpose library)
                "nochatreports:sync", //https://github.com/Aizistral-Studios/No-Chat-Reports (Unsigns chat messages)
                "omegaconfig:sync", //https://github.com/Draylar/omega-config (Config library)
                "openpartiesandclaims:main", //https://github.com/thexaero/open-parties-and-claims (Chunk loading tool, needs server cooperation)
                "owo:config_sync", //https://github.com/wisp-forest/owo-lib (GUI and config library)
                "patchouli:open_book", "patchouli:reload_books", //https://github.com/VazkiiMods/Patchouli/ (Ingame user guide books)
                "pickupnotifier:1/0", "pickupnotifier:1/1", //https://modrinth.com/mod/pick-up-notifier
                "porting_lib:extra_entity_spawn_data", "porting_lib:open_screen", "port_lib:tier_sorting", //https://github.com/Fabricators-of-Create/Porting-Lib (Forge fabric porting library)
                "showmeyourskin:config_sync", //https://github.com/enjarai/show-me-your-skin (Configurable armor rendering)
                "simplerpc:serverconfig", //https://modrinth.com/mod/simple-discord-rpc (Customizable Discord rich presence)
                "skinshuffle:handshake", //https://github.com/IMB11/SkinShuffle (Ingame realtime skin editor)
                "toms_storage:data_s2c", //https://github.com/tom5454/Toms-Storage (Additional storage blocks)
                "travelersbackpack:sync_backpack", "travelersbackpack:update_config", //https://github.com/Tiviacz1337/Travelers-Backpack (Additional backpack items)
                "trinkets:break", "trinkets:sync_inventory", "trinkets:sync_slots", //https://github.com/emilyploszaj/trinkets (Additional armor slots)
                "tweed4:sync_config", //https://github.com/Siphalor/tweed-api (Config library)
                "umu_backpack:load", "umu_backpack:unload", "umu_config:sync_config", //https://github.com/Zemelua/UMU-Backpack (Additional backpack item)
                "visualoverhaul:brewingstand", "visualoverhaul:furnace", "visualoverhaul:record", //https://github.com/TeamMidnightDust/VisualOverhaul (Graphical overhaul for certain blocks)
                "walkietalkie:buttonpressedresponse", //https://github.com/Flaton1/walkie-talkie-mod (Simple voice chat walkietalkie addon)
                "whereisit:s2c_founditem", "whereisit:found_item_s2c", //https://modrinth.com/mod/where-is-it (needs server side component to work)
                "wildfire_gender:hurt", "wildfire_gender:sync", //https://github.com/WildfireRomeo/WildfireFemaleGenderMod (Female player model)
                "cancelblockupdate:channel", //https://github.com/Phoupraw/CancelBlockUpdate/ (Server side block update suppression)
                "replayvoicechat:entity_sound", "replayvoicechat:locational_sound", "replayvoicechat:static_sound", //https://github.com/henkelmax/replay-voice-chat (Record simple voice chat with replay mod)
                "adventure:registered_arg_mappings",
                "better-suggestions:entity_scores_response", "better-suggestions:entity_tags_response", //https://github.com/shurik204/better-suggestions (Better command tab completion)
                "farmingforblockheads:chicken_nest_effect", "farmingforblockheads:market_categories", //https://github.com/TwelveIterationMods/FarmingForBlockheads (Improved farming with new blocks)

                //https://github.com/shedaniel/RoughlyEnoughItems (Crafting recipe helper)
                "roughlyenoughitems:ci_msg", "roughlyenoughitems:request_tags_s2c", "roughlyenoughitems:og_not_enough",
                "roughlyenoughitems:sync_displays",

                //https://modrinth.com/mod/servux
                "servux:structures", "servux:entity_data", "servux:hud_metadata", "servux:debug_service",
                "servux:tweaks",

                //https://github.com/ZsoltMolnarrr/SpellEngine (Magic library)
                "spell_engine:config_sync", "spell_engine:particle_effects", "spell_engine:spell_animation",
                "spell_engine:spell_cooldown", "spell_engine:spell_registry_sync",

                //https://modrinth.com/mod/shulkerboxtooltip
                "shulkerboxtooltip:s2c_handshake", "shulkerboxtooltip:ec_update", "shulkerboxtooltip:ec",
                "shulkerboxtooltip:s2",

                //https://github.com/QuiltMC/quilt-standard-libraries (Quilt general purpose library)
                "qsl:registry_sync/end", "qsl:registry_sync/error_style", "qsl:registry_sync/handshake",
                "qsl:registry_sync/mod_protocol", "qsl:registry_sync/registry_apply", "qsl:registry_sync/registry_data",
                "qsl:registry_sync/registry_restore", "qsl:registry_sync/registry_start",
                "qsl:registry_sync/validate/block_states", "qsl:registry_sync/validate/fluid_states",
                "quilt:extended_entity_spawn_packet", "quilt_registry_entry_attachments:sync",

                //https://github.com/apace100/origins-fabric (Minecraft player special powers mod)
                "origins:badge_list", "origins:confirm_origin", "origins:layer_list", "origins:open_origin_screen",
                "origins:origin_list",

                //https://github.com/skyecodes/IBE-Editor (Item data editor)
                "ibeeditor:network/block_editor_response", "ibeeditor:network/block_inventory_item_editor_response",
                "ibeeditor:network/editor_command", "ibeeditor:network/player_inventory_item_editor_response",
                "ibeeditor:network/entity_inventory_item_editor_response", "ibeeditor:network/server_notification",
                "ibeeditor:network/main_hand_item_editor_response", "ibeeditor:network/entity_editor_response",

                //https://github.com/enjarai/do-a-barrel-roll (Elytra flight visuals changes)
                "do_a_barrel_roll:config_sync", "do_a_barrel_roll:handshake", "do_a_barrel_roll:player_roll",
                "do_a_barrel_roll:server_config_update",

                //https://github.com/CreativeMD/CreativeCore (General purpose library)
                "creativecore:main0", "creativecore:main1", "creativecore:main2", "creativecore:main3",
                "creativecore:main4", "creativecore:main5", "creativecore:main6", "creativecore:main7",
                "creativecore:0s", "creativecore:1s", "creativecore:2s", "creativecore:3s", "creativecore:4s",
                "creativecore:5s", "creativecore:6s", "creativecore:7s",

                //https://github.com/Ladysnake/Cardinal-Components-API (General purpose library)
                "cardinal-components:block_entity_sync", "cardinal-components:chunk_sync",
                "cardinal-components:entity_sync", "cardinal-components:level_sync", "cardinal-components:world_sync",
                "cardinal-components:scoreboard_sync", "cardinal-components:team_sync",

                //https://github.com/RedLime/SpeedRunIGT (Speedrunning overlay)
                "speedrunigt:achieve_advancement", "speedrunigt:achieve_criteria", "speedrunigt:condition_custom",
                "speedrunigt:condition_data", "speedrunigt:timer_category", "speedrunigt:timer_complete",
                "speedrunigt:timer_init", "speedrunigt:timer_start", "speedrunigt:timer_timeline",
                "speedrunigt:timer_uncompleted",

                //https://github.com/miyo6032/bosses-of-mass-destruction (Adds new entities)
                "bosses_of_mass_destruction:change_hitbox", "bosses_of_mass_destruction:charged_ender_pearl_impact",
                "bosses_of_mass_destruction:client_vec3d", "bosses_of_mass_destruction:gauntlet_blindness",
                "bosses_of_mass_destruction:obsidilith_revive", "bosses_of_mass_destruction:player_velocity",
                "bosses_of_mass_destruction:spawn_entity", "bosses_of_mass_destruction:void_blossom_head",
                "bosses_of_mass_destruction:void_blossom_place", "bosses_of_mass_destruction:void_blossom_revive",
                "bosses_of_mass_destruction:void_blossom_spikes", "bosses_of_mass_destruction:void_lily_pollen",

                //https://github.com/AzureDoom/AzureLib (Animation library)
                "azurelib:anim_data_sync", "azurelib:anim_trigger_sync", "azurelibarmor:anim_data_sync",
                "azurelibarmor:anim_trigger_sync", "azurelib:block_entity_anim_data_sync",
                "azurelib:block_entity_anim_trigger_sync", "azurelib:entity_anim_data_sync",
                "azurelib:entity_anim_trigger_sync", "azurelib:s2c_send_config_data", "azurelib:spawn_entity",

                //https://github.com/apace100/apoli (Entity power library)
                "apoli:player_dismount", "apoli:player_mount", "apoli:power_list", "apoli:set_attacker",
                "apoli:sync_power", "apoli:sync_status_effect",

                //https://essential.gg/
                "essential:", "essential:game_rule_hello", "essential:game_rule_permissions",
                "essential:game_rules_changed",

                //https://github.com/MehVahdJukaar/Supplementaries (Additional blocks)
                "supplementaries:0", "supplementaries:1", "supplementaries:2", "supplementaries:3", "supplementaries:4",
                "supplementaries:5", "supplementaries:6", "supplementaries:7", "supplementaries:8", "supplementaries:9",
                "supplementaries:10", "supplementaries:11", "supplementaries:12", "supplementaries:13",
                "supplementaries:14", "supplementaries:24", "supplementaries:25",

                //https://www.curseforge.com/minecraft/mc-mods/waila (Information over block/entity under crosshair)
                "waila:data", "waila:blacklist", "waila:config", "waila:version", "waila:generate_client_dump",

                //https://modrinth.com/plugin/simple-voice-chat (Voice chat)
                "voicechat:secret", "voicechat:joined_group", "voicechat:player_states", "voicechat:player_state",
                "voicechat:remove_category", "voicechat:remove_group", "voicechat:add_category",
                "voicechat:leave_group", "voicechat:create_group", "voicechat:request_secret", "voicechat:set_group",
                "voicechat:update_state", "voicechat:add_group",

                //https://github.com/mim1q/MineCells (Additional dimensions and gamemodes)
                "minecells:obelisk_activation", "minecells:connect", "minecells:crit", "minecells:explosion",
                "minecells:elevator_destroyed", "minecells:spawn_rune_particles", "minecells:sync_minecells_data",

                //https://modrinth.com/mod/appleskin (Additional food bar information)
                "appleskin:exhaustion_sync", "appleskin:saturation_sync", "appleskin:natural_regeneration",
                "appleskin:saturation", "appleskin:exhaustion",

                //https://modrinth.com/mod/puzzles-lib (General purpose library)
                "puzzleslib:play", "puzzleslib:1/0", "puzzleslib:1/1", "puzzleslib:1/2",
                "puzzleslib.main:0", "puzzleslib.main:1", "puzzleslib:play/0",
                //https://github.com/Fuzss/puzzlesapi (General purpose library extension)
                "puzzlesapi:1/1", "puzzlesapi:1/2", "puzzlesapi:1/5", "puzzlesapi:2/1", "puzzlesapi:2/2",

                //https://github.com/bernie-g/geckolib
                "geckolib:block_entity_anim_trigger_sync", "geckolib:entity_anim_trigger_sync",
                "geckolib:entity_data_sync", "geckolib:blockentity_anim_trigger", "geckolib:singleton_data_sync",
                "geckolib:block_entity_anim_data_sync", "geckolib:anim_data_sync", "geckolib:entity_anim_trigger",
                "geckolib:entity_anim_data_sync", "geckolib:anim_trigger_sync", "geckolib:singleton_anim_trigger",
                "geckolib:blockentity_data_sync",

                //https://github.com/Noxcrew/noxesium (MC Championship helper)
                "noxesium:server_rules",
                "noxesium-v1:reset", "noxesium-v1:change_server_rules", "noxesium-v1:server_info",
                "noxesium-v1:mcc_server", "noxesium-v1:mcc_game_state", "noxesium-v1:reset_server_rules",
                "noxesium-v1:stop_sound", "noxesium-v1:start_sound", "noxesium-v1:modify_sound",
                "noxesium-v2:reset", "noxesium-v2:change_server_rules", "noxesium-v2:server_info",
                "noxesium-v2:mcc_server", "noxesium-v2:mcc_game_state", "noxesium-v2:reset_server_rules",
                "noxesium-v2:stop_sound", "noxesium-v2:start_sound", "noxesium-v2:modify_sound",
                "noxesium-v2:change_extra_entity_data", "noxesium-v2:reset_extra_entity_data",

                //https://github.com/Up-Mods/OkZoomer (Zoom mod, deprecated dysfunctional control options)
                "ok_zoomer:force_classic_mode", "ok_zoomer:acknowledge_mod", "ok_zoomer:force_spyglass_overlay",
                "ok_zoomer:force_spyglass", "ok_zoomer:disable_zoom_scrolling", "ok_zoomer:disable_zoom",
                "ok_zoomer:force_zoom_divisor",

                //https://github.com/fzzyhmstrs/fconfig (Synchronizing configuation library)
                "fzzy_config:sync_config_packet", "fzzy_config:config_perms_s2c", "fzzy_config:config_sync_s2c",
                "fzzy_config:config_update_s2c", "fzzy_config:dynamic_id_s2c", "fzzy_config:setting_forward",

                //https://github.com/TwelveIterationMods/CookingForBlockheads (Improved cooking with new blocks)
                "cookingforblockheads:available_craftables_list", "cookingforblockheads:selection_recipes_list",
                "cookingforblockheads:synced_effect",

                //https://github.com/tom5454/CustomPlayerModels (Custom player models)
                "cpm_net:get_skin", "cpm_net:hello", "cpm_net:plugin", "cpm_net:rec_evt", "cpm_net:rec_sfy",
                "cpm_net:set_scl", "cpm_net:set_skin", "cpm_net:srv_anim",

                //https://github.com/Fuzss/forgeconfigapiport
                "forgeconfigapiport:config_file",

                //https://github.com/FrozenBlock/FrozenLib (General purpose library)
                "frozenlib:config_sync_packet", "frozenlib:cooldown_change_packet", "frozenlib:cooldown_change_packet",
                "frozenlib:fading_distance_sound_packet", "frozenlib:flyby_sound_packet", "frozenlib:wind_sync_packet",
                "frozenlib:forced_cooldown_packet", "frozenlib:local_player_sound_packet",
                "frozenlib:local_sound_packet", "frozenlib:moving_fading_restriction_sound_packet",
                "frozenlib:moving_restriction_sound_packet", "frozenlib:remove_entity_screen_shakes_packet",
                "frozenlib:remove_screen_shakes_packet", "frozenlib:screen_shake_entity_packet",
                "frozenlib:screen_shake_packet", "frozenlib:spotting_icon_packet", "frozenlib:wind_disturbance_packet",
                "frozenlib:spotting_icon_remove_packet", "frozenlib:starting_moving_restriction_looping_sound_packet",
                "imm_ptl:remote_cts", //https://www.curseforge.com/minecraft/mc-mods/immersive-portals-mod
                "distant_horizons:message", "tcdcommons:cpn"
        ))
            channelRegisterHandlers.put(channel, player -> {
            });

        //channelRegisterHandlers.put(Lunar.CHANNEL, lunar::sendRestrictions);
        channelRegisterHandlers.put(Feather.CHANNEL, new Feather()::sendRestrictions);
        channelRegisterHandlers.put("openboatutils:settings", player -> send(player, "openboatutils:settings", new byte[]{0, 0})); //https://github.com/o7Moon/OpenBoatUtils/wiki/Packets (Reset packet)
        channelRegisterHandlers.put("itemswapper:disable", player -> send(player, "itemswapper:disable", new byte[]{0})); //https://github.com/tr7zw/ItemSwapper/blob/main/src/main/java/dev/tr7zw/itemswapper/packets/DisableModPayload.java
        channelRegisterHandlers.put("xaerominimap:main", player -> player.sendMessage(Component.text("§n§o§m§i§n§i§m§a§p"))); //https://www.curseforge.com/minecraft/mc-mods/xaeros-minimap
        channelRegisterHandlers.put("xaeroworldmap:main", player -> player.sendMessage(Component.text("§n§o§m§i§n§i§m§a§p"))); //https://www.curseforge.com/minecraft/mc-mods/xaeros-minimap
        channelRegisterHandlers.put("litemoretica:init_easy_place", player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "litematica")); //https://github.com/Earthcomputer/litemoretica/tree/master
        channelRegisterHandlers.put("litemoretica:init_ea", player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "litematica")); //https://github.com/Earthcomputer/litemoretica/tree/master
        channelRegisterHandlers.put("servux:litematics", player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "litematica")); //https://github.com/maruohon/litematica/blob/pre-rewrite/fabric/1.21.1-masa/src/main/java/fi/dy/masa/litematica/network/ServuxLitematicaHandler.java
        channelRegisterHandlers.put("voxelmap:settings", player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "voxelmap")); //https://modrinth.com/mod/voxelmap-updated undocumented
        channelRegisterHandlers.put("inventoryessentials:hello", player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "InventoryEssentials")); //https://github.com/TwelveIterationMods/InventoryEssentials
        channelRegisterHandlers.put(Controlify.CHANNEL, new Controlify()::onRegister);

        for (String channel : Arrays.asList(
                "worldinfo:world_id", // JourneyMap and VoxelMap
                "journeymap:version", "journeymap:admin_req", "journeymap:mp_options_req", "journeymap:waypoint",
                "journeymap:player_loc", "journeymap:admin_save", "journeymap:teleport_req", "journeymap:common",
                "journeymap:perm_req", "journeymap:chunk_overlay", "journeymap:remove_player"
        ))
            channelRegisterHandlers.put(channel, player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "minimap"));

        // Sending the restrictions currently does not work or the client ignores it
        channelRegisterHandlers.put("norisk:main", player -> {
            Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "norisk client");
        });

        for (String channel : Arrays.asList(
                "flashback:remote_food_data",
                "flashback:remote_set_slot",
                "flashback:force_client_tick",
                "flashback:accurate_entity_position",
                "flashback:instantly_lerp",
                "flashback:remote_experience",
                "flashback:clear_particles",
                "flashback:set_border_lerp_start_time",
                "flashback:remote_select_hotbar_slot", // https://github.com/Moulberry/Flashback/tree/master/src/main/java/com/moulberry/flashback/packet
                "flashback:clear_entities", // https://github.com/Moulberry/Flashback
                Lunar.CHANNEL
        ))
            channelRegisterHandlers.put(channel, player -> {
                if (!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.RESTRICTED_MODS)) {
                    Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "flashback");
                }
            });

        for (String channel : Arrays.asList("bedrockify:cauldron_particles", "bedrockify:eat-particles")) {
            //https://github.com/juancarloscp52/BedrockIfy (Bedrock features on Java, banned for reach-around block placement)
            channelRegisterHandlers.put(channel, player -> Chatter.disconnect(player).prefixless("MOD_YELLOW_SING", "bedrockify"));
        }

        registerBiDirPassthrough("WECUI", "worldedit:cui", "worldedit:internal", "minecraft:wecui");

        registerPassthroughToClient(
                "axiom:enable", "axiom:initialize_hotbars",
                "axiom:response_chunk_data", "axiom:register_world_properties", "axiom:set_world_property",
                "axiom:ack_world_properties", "axiom:restrictions", "axiom:marker_data", "axiom:marker_nbt_response",
                "axiom:custom_blocks", "axiom:editor_warning", "axiom:blueprint_manifest", "axiom:response_blueprint",
                "axiom:annotation_update", "axiom:redo_handshake", "axiom:register_custom_items", "axiom:register_custom_block_v2",
                "axiom:register_world"
        );
        registerBiDirPassthrough("axiom:handle_big_payload", "axiom:set_editor_views", "axiom:update_available_dispatch_sends");
        for (String channel : Arrays.asList(
                "axiom:hello", "axiom:set_gamemode", "axiom:set_fly_speed", "axiom:set_world_time",
                "axiom:set_world_property", "axiom:set_block", "axiom:set_hotbar_slot", "axiom:switch_active_hotbar",
                "axiom:teleport", "axiom:request_chunk_data", "axiom:spawn_entity", "axiom:response_entity_data",
                "axiom:manipulate_entity", "axiom:delete_entity", "axiom:marker_nbt_request", "axiom:set_buffer",
                "axiom:allowed_gamemodes", "axiom:ignore_display_entities", "axiom:add_server_heightmap", "axiom:set_no_physical_trigger",
                "axiom:request_blueprint", "axiom:request_entity_data", "axiom:upload_blueprint"
        )) {
            channelRegisterHandlers.put(channel, player -> {
            });
            registerPassthroughToServer(channel);
        }

        for (String channel : Arrays.asList(
                "floodgate:skin",
                "watut:nbt", "watut:nbt_server", //https://github.com/Corosauce/WATUT
                "bclib:hello_server",
                "vivecraft:data", //https://github.com/Vivecraft/VivecraftMod https://github.com/jrbudda/Vivecraft_Spigot_Extensions https://github.com/Techjar/Vivecraft_BungeeCord_Extensions (VR support)
                "badpackets:channel_sync", //https://github.com/badasintended/badpackets (Forge fabric translation layer)
                "hero-api:mouse-packet", "hero-api:mouse-scroll", //TODO https://github.com/NoRiskClient Mit Superhelden-Mod?
                "hyevent:location", "hyevent:register", // Hypixel mods (I could not find which sadly)
                "hypixel:hello", "hypixel:party_info", "hypixel:ping", "hypixel:player_info", "hypixel:register", "hypixel:party_info", "hypixel:player_info", "hypixel:ping" // Hypixel mods (I could not find which sadly)
        )) {
            channelRegisterHandlers.put(channel, player -> {
            });
            register(channel, false, directional(UNKNOWN, DROP));
        }

        for (String channel : Arrays.asList(
                "UNREGISTER", "minecraft:unregister", // used by carpet and servux
                "WDL|REQUEST", "wdl:request",
                "minecraft:intave",  //undocumented, byte stringlength, clientconfig, byte length, json {"legacySneakHeight":false,"legacyOldRange":false,"legacyOldSlowdown":false}
                "waila:entity", "waila:block",
                "lambdacontrols:hello",
                "midnightcontrols:controls_mode",
                "inventorysorter:sync_settings_packet",
                "voicechat:request_secret", "voicechat:update_state",
                "shulkerboxtooltip:c2s_handshake",
                "noxesium:client_information", "noxesium:client_settings",
                "polymer:handshake", //https://github.com/Patbox/polymer
                "minecells:request_sync_minecells_data",
                "puzzlesapi:1/4",
                "fancymenu:variable_cmd_sugg", "fancymenu:packet_bridge",
                "jade:request_entity", "jade:request_tile",
                "tiscm:network/v1", //https://github.com/gnembon/fabric-carpet
                "couplings:client_config",
                "yigd:config_update_c2s",
                "kiwi:set_cosmetic",
                "fwaystones:request_player_waystone_update",
                "openboatutils:settings", //https://github.com/o7Moon/OpenBoatUtils
                "block-event-separator:handshake", //https://github.com/SpaceWalkerRS/block-event-separator (Separating block events)
                "oth3r-sit:settings_v1.1", //https://github.com/Oth3r/Sit (Sitting mod)
                "carryon:key_pressed" //https://github.com/Tschipp/CarryOn/blob/1.21/Common/src/main/java/tschipp/carryon/networking/serverbound/ServerboundCarryKeyPressedPacket.java
        ))
            register(channel, false, directional(UNKNOWN, DROP));

        register("REGISTER", false, directional(this::serverRegistersChannel, UNKNOWN));
        register("minecraft:register", false, directional(this::serverRegistersChannel, UNKNOWN));

        register("MC|Brand", false, directional(this::steamWarBrand, UNKNOWN));
        register("minecraft:brand", false, directional(this::steamWarBrand, UNKNOWN));

        //Needs to be registered cause paper refuses to send PluginMessages on unregistered channels...
        register("sw:bridge", true, directional(async(event -> NetworkPacket.handle(new ServerMetaInfo((ServerConnection) event.getSource()), event.getData())), UNKNOWN));
        registerPassthroughToServer("sw:hotkeys");
        register("fabricmodsender:mods", true, directional(UNKNOWN, async(new FabricModSender()::handlePluginMessage)));

        register("WDL|INIT", true, directional(UNKNOWN, wdl::handlePluginMessage));
        register("wdl:init", true, directional(UNKNOWN, wdl::handlePluginMessage));

        register(Lunar.CHANNEL, true, async(lunar::handlePluginMessage));
        register(Alpine.HANDSHAKE, false, directional(UNKNOWN, new Alpine()::handleHandshakeMessage));
        register("LMC", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
        register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
        register("labymod:neo", false, directional(UNKNOWN, DROP)); //undocumented, JSON format "0" byte, packetlängen byte, {"version":"4.1.25"}
        register(FML.CHANNEL, true, directional(UNKNOWN, async(new FML()::handlePluginMessage)));

        //vanilla does not register any channels (sends only one minecraft:brand vanilla, nothing else (potential spoofed client detection))
        //Forge interestingly registers all channels the server registers
        //meteor https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/ServerSpoof.java https://github.com/MeteorDevelopment/meteor-client/blob/master/src/main/java/meteordevelopment/meteorclient/systems/modules/misc/DiscordPresence.java
        //litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65
        // Hackclientlike modsuppressor for labymod: https://github.com/Neocraftr/LabyMod-NeoEssentials (Potentially recognizable from NO Addons/NO Mods?) https://github.com/Neocraftr/LabyMod-NeoEssentials/blob/master/src/main/java/de/neocraftr/neoessentials/utils/BytecodeMethods.java
    }

    @Subscribe
    public void onClientChannelRegister(PlayerChannelRegisterEvent event) {
        Player player = event.getPlayer();

        for (ChannelIdentifier channel : event.getChannels()) {
            channelRegisterHandlers.getOrDefault(channel.getId(), p -> VelocityCore.getLogger().log(Level.WARNING, () -> p.getUsername() + " registered unknown channel " + channel)).accept(player);
        }
    }

    @Subscribe
    public void onPluginMessage(PluginMessageEvent event) {
        event.setResult(PluginMessageEvent.ForwardResult.handled());

        try {
            handlers.getOrDefault(event.getIdentifier().getId(), UNKNOWN).handle(event);
        } catch (Exception e) {
            throw new SecurityException("PluginMessage handling exception: " + event + "\n" + Arrays.toString(event.getData()), e);
        }
    }

    @Subscribe
    public void onClientBrand(PlayerClientBrandEvent event) {
        Player player = event.getPlayer();

        String brand = event.getBrand();

        VelocityCore.getLogger().log(knownBrands.contains(brand) ? Level.INFO : Level.WARNING, () -> player.getUsername() + " joins with brand: " + brand);
        if (brand.equals("badlion")) {
            badlion.sendRestrictions(player);
        }
    }

    private void registerPassthroughToClient(String... channels) {
        for (String channel : channels) {
            channelRegisterHandlers.put(channel, player -> {
            });
            register(channel, false, directional(PASS_THROUGH, UNKNOWN));
        }
    }

    private void registerPassthroughToServer(String... channels) {
        for (String channel : channels) {
            register(channel, false, directional(UNKNOWN, PASS_THROUGH));
        }
    }

    private void registerBiDirPassthrough(String... channels) {
        for (String channel : channels) {
            channelRegisterHandlers.put(channel, player -> {
            });
            register(channel, false, PASS_THROUGH);
        }
    }

    private void register(String channel, boolean clientSideRegister, Parser handler) {
        handlers.put(channel, handler);
        if (clientSideRegister) {
            VelocityCore.getProxy().getChannelRegistrar().register(channel.indexOf(':') != -1 ? MinecraftChannelIdentifier.from(channel) : new LegacyChannelIdentifier(channel));
        }
    }

    private void serverRegistersChannel(PluginMessageEvent event) {
        Player player = (Player) event.getTarget();

        List<String> channels = new ArrayList<>(Arrays.asList(new String(event.getData()).split("\0")));
        channels.removeIf(channel -> channel.equals("sw:bridge"));
        send(player, "REGISTER", "minecraft:register", String.join("\0", channels).getBytes());
    }

    private void steamWarBrand(PluginMessageEvent event) {
        Player player = (Player) event.getTarget();
        String brand = Chatter.of(player).parseToLegacy("STEAMWAR_BRAND", "Velocity", player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(""), new String(event.getData(), 1, event.getData().length - 1));
        player.sendPluginMessage(event.getIdentifier(), genBufPacket(buf -> ProtocolUtils.writeString(buf, brand)));
    }


    private Parser directional(Parser fromServer, Parser fromPlayer) {
        return event -> {
            if (event.getSource() instanceof Player) {
                fromPlayer.handle(event);
            } else {
                fromServer.handle(event);
            }
        };
    }

    private Parser async(Parser parser) {
        return event -> VelocityCore.schedule(() -> parser.handle(event)).schedule();
    }

    private interface Parser {
        void handle(PluginMessageEvent event);
    }
}
