package jesse.keeblarcraft.Commands;

import java.util.List;

import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;

import jesse.keeblarcraft.FactionMgr.FactionManager;
import jesse.keeblarcraft.FactionMgr.Callbacks.PlayerCommandFlightCallback;
import jesse.keeblarcraft.FactionMgr.FactionTier.FactionTierEnum;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;

public class FactionCommands {

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         RegisterFactionCommands
    ///
    /// @brief      Registers all commands for factions
    /////////////////////////////////////////////////////////////////////////////
    public void RegisterFactionCommands() {
        CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
            var factionNode = CommandManager.literal("faction").build();

            var createFaction = CommandManager.literal("create").build();
            var disbandFaction = CommandManager.literal("disband").build();
            var promote = CommandManager.literal("promote").build();
            var demote = CommandManager.literal("demote").build();
            var kick = CommandManager.literal("kick").build();
            var set = CommandManager.literal("set").build();
            var get = CommandManager.literal("get").build();
            var power = CommandManager.literal("power").build();
            var info = CommandManager.literal("info")
                .executes(context -> GetFactionInformation(context.getSource().getPlayer()))
                .build();
            var invite = CommandManager.literal("invite").build();
            var fly = CommandManager.literal("fly")
                .executes(context -> ForwardFlightCallback(context)).build();

            // The below nodes are duplicates but are necessary to make the execute path jump correctly
            var createFactionName = CommandManager.argument("faction_name", StringArgumentType.greedyString())
                .executes(context -> CreateFaction
                                    (
                                    context,
                                    StringArgumentType.getString(context, "faction_name")
                                    )
                ).build();

            var disbandFactionName = CommandManager.argument("faction_name", StringArgumentType.greedyString())
                .executes(context -> DeleteFaction
                                    (
                                    context,
                                    StringArgumentType.getString(context, "faction_name")
                                    )
                ).build();

            var promoteName = CommandManager.argument("target_name", EntityArgumentType.player())
                .executes(context -> PromotePlayerInFaction(context, EntityArgumentType.getPlayer(context, "target_name")))
                .build();
            var demoteName = CommandManager.argument("target_name", EntityArgumentType.player())
                .executes(context -> DemotePlayerInFaction(context, EntityArgumentType.getPlayer(context, "target_name")))
                .build();
            var kickName = CommandManager.argument("target_name", EntityArgumentType.player())
                .executes(context -> KickPlayerFromFaction(context, EntityArgumentType.getPlayer(context, "target_name")))
                .build();
            var inviteName = CommandManager.argument("target_name", EntityArgumentType.player())
                .executes(context -> InvitePlayerToFaction(context, EntityArgumentType.getPlayer(context, "target_name")))
                .build();

            var setPower = CommandManager.literal("power").build();
            var setPowerAmnt = CommandManager.argument("power_amount", IntegerArgumentType.integer()).build();
            var setPowerName = CommandManager.argument("set_power_name", StringArgumentType.string())
                .executes(context -> SetFactionPower(context.getSource().getPlayer(), StringArgumentType.getString(context, "set_power_name"), IntegerArgumentType.getInteger(context, "power_amount")))
                .build();

            var getPowerName = CommandManager.argument("get_power_name", StringArgumentType.string())
                .executes(context -> GetFactionPower(context.getSource().getPlayer(), StringArgumentType.getString(context, "get_power_name"))).build();

            var leaveFaction = CommandManager.literal("leave").executes(context -> LeaveFaction(context)
            ).build();

            var listAll = CommandManager.literal("list")
            .executes(context -> ListAllFactions(context.getSource().getPlayer())).build();


            // Root node
            dispatcher.getRoot().addChild(factionNode);

            // List command
            factionNode.addChild(listAll);
            factionNode.addChild(createFaction);
            factionNode.addChild(disbandFaction);
            factionNode.addChild(leaveFaction);
            factionNode.addChild(promote);
            factionNode.addChild(demote);
            factionNode.addChild(kick);
            factionNode.addChild(info);
            factionNode.addChild(invite);
            factionNode.addChild(fly);
            factionNode.addChild(set);
            factionNode.addChild(get);

            get.addChild(power);
            power.addChild(getPowerName);

            set.addChild(setPower);
            setPower.addChild(setPowerAmnt);
            setPowerAmnt.addChild(setPowerName);

            promote.addChild(promoteName);
            demote.addChild(demoteName);
            kick.addChild(kickName);
            invite.addChild(inviteName);

            createFaction.addChild(createFactionName);
            disbandFaction.addChild(disbandFactionName);
        });

        // I'll refactor the above one later! LOL
        CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
            var factionNode = CommandManager.literal("faction").build();
            var powerNode = CommandManager.literal("power")
                .executes(context -> GetFactionPower(context.getSource().getPlayer()))
                .build();
            var setNode = CommandManager.literal("set").build();
            var factionNameNode = CommandManager.argument("faction_name", StringArgumentType.string()).build();

            var powerAmountNode = CommandManager.argument("power_amount", IntegerArgumentType.integer())
                .executes(context -> SetFactionPower(context.getSource().getPlayer(), StringArgumentType.getString(context, "faction_name"), IntegerArgumentType.getInteger(context, "power_amount")))
                .build();


            dispatcher.getRoot().addChild(factionNode);
            factionNode.addChild(powerNode);

            powerNode.addChild(setNode);
            setNode.addChild(factionNameNode);
            setNode.addChild(powerAmountNode);
        });
    }

    private int ForwardFlightCallback(CommandContext<ServerCommandSource> context) {
        if (context.getSource().isExecutedByPlayer()) {
            ServerPlayerEntity player = context.getSource().getPlayer();
            ServerWorld world = context.getSource().getWorld();
            Integer fPower = FactionManager.GetInstance().GetFactionPower(player);
            FactionTierEnum fTier = FactionManager.GetInstance().GetFactionTier(FactionManager.GetInstance().GetFactionOfPlayer(player.getUuidAsString()));


            ActionResult result = PlayerCommandFlightCallback.EVENT.invoker().interact(player, world, fPower, fTier);
        }
        return 0;
    }

    public int SetFactionPower(ServerPlayerEntity caller, String faction, Integer amount) {
        Boolean success = FactionManager.GetInstance().SetFactionPower(caller, faction, amount);

        if (success) {
            caller.sendMessage(Text.of("Successfully set the faction " + faction + " power to " + amount));
        } else {
            caller.sendMessage(Text.of("This is an operator only command!"));
        }

        return 0;
    }

    public int GetFactionPower(ServerPlayerEntity player) {
        FactionManager.GetInstance().GetFactionPower(player);
        return 0;
    }

    public int GetFactionPower(ServerPlayerEntity player, String factionName) {
        Integer amnt = FactionManager.GetInstance().GetFactionPower(factionName);
        player.sendMessage(Text.of("[" + factionName + " - POWER]:" + amnt));
        return 0;
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         CreateFaction
    ///
    /// @param[in]  context is the context of where this command runs from
    ///
    /// @param[in]  newFactionName is the faction name to be created
    ///
    /// @brief      Command to create a faction
    /////////////////////////////////////////////////////////////////////////////
    private int CreateFaction(CommandContext<ServerCommandSource> context, String newFactionName) {
        int retValue = 0;

        System.out.println("CreateFaction getting player obj");
        ServerPlayerEntity player = context.getSource().getPlayer();
        System.out.println("CreateFaction called");
        if (newFactionName.length() >= 4) {
            FactionManager.GetInstance().CreateFaction(newFactionName, player);
        } else {
            player.sendMessage(Text.of("Your faction must be at least 4 letters long!"));
        }

        return retValue;
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         DeleteFaction
    ///
    /// @param[in]  context is the context of where this command runs from
    ///
    /// @param[in]  newFactionName is the faction name to be deleted
    ///
    /// @brief      Command to create a faction
    /////////////////////////////////////////////////////////////////////////////
    private int DeleteFaction(CommandContext<ServerCommandSource> context, String factionName) {
        int retValue = 0;

        ServerPlayerEntity player = context.getSource().getPlayer();
        FactionManager.GetInstance().DeleteFaction(factionName, player);

        return retValue;
    }

    private int InvitePlayerToFaction(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) {
        FactionManager.GetInstance().InvitePlayerToFaction(context.getSource().getPlayer(), player.getUuidAsString(), player.getEntityName());
        return 0;
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         LeaveFaction
    ///
    /// @param[in]  context is the context of where this command runs from
    ///
    /// @brief      Command to leave a faction
    /////////////////////////////////////////////////////////////////////////////
    private int LeaveFaction(CommandContext<ServerCommandSource> context) {
        ServerPlayerEntity player = context.getSource().getPlayer();

        FactionManager.GetInstance().LeaveFaction(player);

        return 0;
    }

    private int GetFactionInformation(ServerPlayerEntity player) {
        return 0;
    }

    private int KickPlayerFromFaction(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) {
        int retValue = -1;

        return retValue;
    }

    private int PromotePlayerInFaction(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) {
        ServerPlayerEntity caller = context.getSource().getPlayer();
        FactionManager.GetInstance().PromotePlayer(caller, player.getUuidAsString(), player.getEntityName());
        return 0;
    }

    private int DemotePlayerInFaction(CommandContext<ServerCommandSource> context, ServerPlayerEntity player) {
        ServerPlayerEntity caller = context.getSource().getPlayer();
        FactionManager.GetInstance().DemotePlayer(caller, player.getUuidAsString(), player.getEntityName());
        return 0;
    }

    private int SetPlayerRoleInFaction() {
        int retValue = -1;

        return retValue;
    }

    private int DeclareFactionBase() {
        int retValue = -1;

        return retValue;
    }

    private int DeclareFactionEvent() {
        int retValue = -1;

        return retValue;
    }

    private int DeclareEnemyFaction() {
        int retValue = -1;

        return retValue;
    }

    private int DeclareAlliedFaction() {
        int retValue = -1;

        return retValue;
    }

    private int DeclareNeutralFaction() {
        int retValue = -1;

        return retValue;
    }

    /////////////////////////////////////////////////////////////////////////////
    /// @fn         ListAllFactions
    ///
    /// @param[in]  player is the command runner
    ///
    /// @brief      Command to see all factions on the server
    /////////////////////////////////////////////////////////////////////////////
    private int ListAllFactions(ServerPlayerEntity player) {
        int retValue = -1;

        System.out.println("Listing factions");
        List<String> facs = FactionManager.GetInstance().ListOfFactions();

        if (facs != null) {
            player.sendMessage(Text.of("All factions on server: " + facs));
        } else {
            player.sendMessage(Text.of("There are no factions on this server yet!"));
        }

        return retValue;
    }

    private int RegisterFactionChatChannel() {
        int retValue = -1;

        return retValue;
    }

    // admin only
    private int DeleteFactionChatChannel() {
        int retValue = -1;

        return retValue;
    }

    private int DepositToFactionBank() {
        int retValue = -1;

        return retValue;
    }

    private int WithdrawFromFactionBank() {
        int retValue = -1;

        return retValue;
    }

    private int FactionBankBalance() {
        int retValue = -1;

        return retValue;
    }

    private int ListFactionBankTransactions() {
        int retValue = -1;

        return retValue;
    }
}