/*
 *   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 de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.command.TypeValidator;
import de.steamwar.data.BauLockState;
import de.steamwar.linkage.EventMode;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.messages.Chatter;
import de.steamwar.messages.Message;
import de.steamwar.messages.PlayerChatter;
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
import de.steamwar.persistent.Bauserver;
import de.steamwar.sql.BauweltMember;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.velocitycore.ServerStarter;
import de.steamwar.velocitycore.ServerVersion;
import de.steamwar.velocitycore.SubserverSystem;
import de.steamwar.velocitycore.VelocityCore;
import de.steamwar.velocitycore.inventory.SWInventory;
import de.steamwar.velocitycore.inventory.SWItem;
import de.steamwar.velocitycore.network.NetworkSender;
import de.steamwar.velocitycore.util.BauLock;

import java.util.Collection;
import java.util.function.Consumer;

@Linked
@EventMode(false)
public class BauCommand extends SWCommand {

    @LinkedInstance
    private HelpCommand command;

    public BauCommand() {
        super("bau", "b", "build", "gs");
    }

    @Register(noTabComplete = true)
    public void genericHelp(Chatter sender, String... args) {
        this.command.sendBauHelp(sender);
    }

    @Register
    public void toBau(PlayerChatter sender, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
        new ServerStarter().build(version, sender.user().getUUID()).send(sender.getPlayer()).start();
    }

    @Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
    public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
        BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
        sender.system("BAU_ADDMEMBER_ADDED");
        Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
    }

    @Validator(value = "addMemberTarget", local = true)
    public TypeValidator<SteamwarUser> addMemberTargetValidator() {
        return (sender, value, messageSender) -> {
            if (value == null) {
                messageSender.send("UNKNOWN_PLAYER");
                return false;
            }
            if (sender.user().getUUID().equals(value.getUUID())) {
                messageSender.send("BAU_ADDMEMBER_SELFADD");
                return false;
            }
            if (BauweltMember.getBauMember(sender.user().getId(), value.getId()) != null) {
                messageSender.send("BAU_ADDMEMBER_ISADDED");
                return false;
            }
            return true;
        };
    }

    @Register(value = "tp", description = "BAU_TP_USAGE")
    @Register("teleport")
    public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
        new ServerStarter().build(version, worldOwner.getUUID()).send(sender.getPlayer()).start();
    }

    @Validator(value = "teleportTarget", local = true)
    public TypeValidator<SteamwarUser> teleportTargetValidator() {
        return (sender, owner, messageSender) -> {
            if (owner == null) {
                messageSender.send("UNKNOWN_PLAYER");
                return false;
            }
            if (sender.user().getId() != owner.getId() && BauweltMember.getBauMember(owner.getId(), sender.user().getId()) == null) {
                SubserverSystem.sendDeniedMessage(sender, owner.getUUID());
                messageSender.send("BAU_TP_NOALLOWED");
                return false;
            }

            if (BauLock.isLocked(owner, sender.user())) {
                messageSender.send("BAU_LOCKED_NOALLOWED");
                Chatter.of(owner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
                return false;
            }
            return true;
        };
    }

    @Register("info")
    public void info(PlayerChatter sender) {
        sender.getPlayer().spoofChatInput("/bauinfo");
    }

    @Register("setspectator")
    public void setSpectator(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
        setPerms(sender, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
            member.setBuild(false);
            member.setSupervisor(false);
        });
    }

    @Register("setbuilder")
    public void setBuilder(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
        setPerms(sender, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
            member.setBuild(true);
            member.setSupervisor(false);
        });
    }

    @Register("setsupervisor")
    public void setSupervisor(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
        setPerms(sender, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
            member.setBuild(true);
            member.setSupervisor(true);
        });
    }

    private void setPerms(Chatter owner, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
        if (user == null) {
            owner.system("BAU_MEMBER_SET_USAGE", name);
            return;
        }

        withMember(owner, user, target -> {
            setter.accept(target);

            Bauserver bauserver = Bauserver.get(owner.user().getUUID());
            if (bauserver != null) {
                bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
            }

            Chatter.of(user.getUUID()).system("BAU_MEMBER_SET_TARGET", owner, new Message(permName));
            owner.system("BAU_MEMBER_SET", new Message(permName));
        });
    }

    @Register(value = "delmember", description = "BAU_DELMEMBER_USAGE")
    public void delmember(Chatter owner, @Mapper("addedUsers") SteamwarUser user) {
        withMember(owner, user, target -> {
            target.remove();

            Bauserver bauserver = Bauserver.get(owner.user().getUUID());
            Chatter member = Chatter.of(user.getUUID());
            member.withPlayer(player -> {
                if (bauserver != null && bauserver.getRegisteredServer().getPlayersConnected().contains(player)) {
                    player.createConnectionRequest(VelocityCore.get().getConfig().lobbyserver()).fireAndForget();
                }
            });

            if (bauserver != null) {
                bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
            }

            member.system("BAU_DELMEMBER_DELETED_TARGET", owner);
            owner.system("BAU_DELMEMBER_DELETED");
        });
    }

    @Mapper(value = "addedUsers", local = true)
    public TypeMapper<SteamwarUser> addedUsers() {
        return new TypeMapper<SteamwarUser>() {
            @Override
            public SteamwarUser map(Chatter sender, PreviousArguments previousArguments, String s) {
                return SteamwarUser.get(s);
            }

            @Override
            public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
                return BauweltMember.getMembers(sender.user().getId()).stream()
                        .map(bauweltMember -> SteamwarUser.byId(bauweltMember.getMemberID()).getUserName())
                        .toList();
            }
        };
    }

    @Register("stop")
    public void stop(PlayerChatter sender) {
        VelocityCore.schedule(() -> {
            sender.system("BAU_STOPPING");
            Bauserver subserver = Bauserver.get(sender.user().getUUID());
            if (subserver != null) {
                subserver.stop();
            }

            sender.system("BAU_STOPPED");
        }).schedule();
    }

    @Register("resetall")
    @Register("delete")
    public void delete(PlayerChatter sender, ServerVersion version) {
        SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
        inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
            String world = version.getWorldFolder(ServerStarter.WORLDS_BASE_PATH) + sender.user().getId();

            VelocityCore.schedule(() -> {
                Bauserver subserver = Bauserver.get(sender.user().getUUID());
                if (subserver != null) {
                    subserver.stop();
                }

                SubserverSystem.deleteFolder(VelocityCore.local, world);
                sender.system("BAU_DELETE_DELETED");
            }).schedule();

            inventory.close();
        });
        inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
        inventory.open();
    }

    @Register("test")
    @Register("testarena")
    public void testarena(PlayerChatter sender, @Mapper("nonHistoricArenaMode") @OptionalValue("") @AllowNull GameModeConfig<String, String> arenaMode, @Mapper("arenaMap") @OptionalValue("") @AllowNull String map) {
        FightCommand.createArena(sender, "/bau testarena ", false, arenaMode, map, false, (chatter, mode, m) ->
                new ServerStarter().test(mode, m, sender.getPlayer()).start()
        );
    }

    @Register(value = "lock", description = "BAU_LOCKED_OPTIONS")
    public void lock(Chatter sender, BauLockState bauLockState) {
        BauLock.setLocked(sender, bauLockState);
    }

    @Register("unlock")
    public void unlock(Chatter sender) {
        BauLock.setLocked(sender, BauLockState.OPEN);
    }

    private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
        if (member == null) {
            owner.system("UNKNOWN_PLAYER");
            return;
        }

        BauweltMember target = BauweltMember.getBauMember(owner.user().getId(), member.getId());
        if (target == null) {
            owner.system("BAU_MEMBER_NOMEMBER");
            return;
        }

        function.accept(target);
    }
}
