/*
 *  Copyright 2025-2026 R3944Realms
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package top.r3944realms.eroticdungeongame.content.entity.npc;

import com.google.common.collect.Maps;
import com.mojang.authlib.GameProfile;
import dev.dubhe.curtain.utils.Messenger;
import net.minecraft.core.BlockPos;
import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.*;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.TickTask;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.GameProfileCache;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.entity.ai.goal.GoalSelector;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.food.FoodData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.eroticdungeongame.EroticDungeon;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.control.NPCBodyRotationControl;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.control.NPCJumpControl;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.control.NPCLookControl;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.control.NPCMoveControl;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.goal.NPCFindTargetGoal;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.goal.NPCFollowGoal;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.navigation.NPCGroupGroundPathNavigation;
import top.r3944realms.eroticdungeongame.content.entity.npc.ai.navigation.NPCPathNavigation;
import top.r3944realms.eroticdungeongame.core.network.NPCEmptyClientConnection;
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
import top.r3944realms.lib39.util.nbt.NBTWriter;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;

public class NPCServerPlayer extends ServerPlayer implements INPCPlayer {
    public static final String PREFIX = "[NPC]";
    public String npcNameWithoutPrefix;

    @Nullable
    private LivingEntity target;
    private final GoalSelector goalSelector;
    private final GoalSelector targetSelector;

    protected NPCLookControl lookControl;
    protected NPCMoveControl moveControl;
    protected NPCJumpControl jumpControl;
    private final NPCBodyRotationControl bodyRotationControl;

    private final Map<BlockPathTypes, Float> pathfindingMalus = Maps.newEnumMap(BlockPathTypes.class);

    protected NPCPathNavigation navigation;

    public String getNpcNameWithoutPrefix() {
        return npcNameWithoutPrefix;
    }

    public void setNpcNameWithoutPrefix(String npcNameWithoutPrefix) {
        this.npcNameWithoutPrefix = npcNameWithoutPrefix;
    }

    public Runnable fixStartingPosition = () -> {
    };

    public NPCServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile) {
        super(server, level, gameProfile);
        this.goalSelector = new GoalSelector(server::getProfiler);
        this.targetSelector = new GoalSelector(server::getProfiler);
        this.navigation = this.createNavigation(level);
        this.lookControl = new NPCLookControl(this);
        this.moveControl = new NPCMoveControl(this);
        this.jumpControl = new NPCJumpControl(this);
        this.bodyRotationControl = this.createBodyControl();
        getAdvancements().stopListening();
        registerGoals();
    }

    protected NPCPathNavigation createNavigation(Level level) {
        return new NPCGroupGroundPathNavigation(this, level);
    }

    protected boolean shouldPassengersInheritMalus() {
        return false;
    }

    public float getPathfindingMalus(BlockPathTypes nodeType) {
        Float f = this.pathfindingMalus.get(nodeType);
        return f == null ? nodeType.getMalus() : f;
    }

    public void setPathfindingMalus(BlockPathTypes nodeType, float malus) {
        this.pathfindingMalus.put(nodeType, malus);
    }

    public void onPathfindingStart() {
    }

    public void onPathfindingDone() {
    }


    protected void registerGoals() {
        // 优先级 0 最高
        this.goalSelector.addGoal(1, new NPCFindTargetGoal(this, 16, 100));
        this.goalSelector.addGoal(2, new NPCFollowGoal(this, 1.0, 3, 2));
    }

    protected NPCBodyRotationControl createBodyControl() {
        return new NPCBodyRotationControl(this);
    }

    public double getFollowDistance() {
        return 2;
    }

    public int getMaxHeadXRot() {
        return 40;
    }

    public int getMaxHeadYRot() {
        return 75;
    }

    public int getHeadRotSpeed() {
        return 10;
    }

    @SuppressWarnings("UnusedReturnValue")
    public static @Nullable NPCServerPlayer createNPC(String username, @NotNull MinecraftServer server, double x, double y, double z, double yaw, double pitch, ResourceKey<Level> dimensionId, GameType gamemode, boolean isflying) {
        ServerLevel worldIn = server.getLevel(dimensionId);
        if (worldIn != null) {
            GameProfileCache.setUsesAuthentication(false);
            GameProfile gameProfile = null;
            try {
                GameProfileCache profileCache = server.getProfileCache();
                if (profileCache != null) {
                    gameProfile = profileCache.get(username).orElse(null);
                }
            } finally {
                GameProfileCache.setUsesAuthentication(server.isDedicatedServer() && server.usesAuthentication());
            }
            try {
                if (gameProfile == null) {
                    gameProfile = new GameProfile(UUIDUtil.createOfflinePlayerUUID(username), username);
                }

                if (gameProfile.getProperties().containsKey("textures")) {
                    AtomicReference<GameProfile> result = new AtomicReference<>();
                    Objects.requireNonNull(result);
                    SkullBlockEntity.updateGameprofile(gameProfile, result::set);
                    gameProfile = result.get();
                }
                NPCServerPlayer instance = new NPCServerPlayer(server, worldIn, gameProfile);
                instance.fixStartingPosition = () -> {
                    instance.moveTo(x, y, z, (float) yaw, (float) pitch);
                };
                ((IEDGMinecraftServer) server).getNPCPlayerList().placeNewNPC(new NPCEmptyClientConnection(PacketFlow.SERVERBOUND), instance);
                instance.teleportTo(worldIn, x, y, z, (float) yaw, (float) pitch);
                instance.setHealth(20.0F);
                instance.unsetRemoved();
                instance.setMaxUpStep(0.6F);
                instance.gameMode.changeGameModeForPlayer(gamemode);
                ((IEDGMinecraftServer) server).getNPCPlayerList().broadcastAll(new ClientboundRotateHeadPacket(instance, (byte) ((int) (instance.yHeadRot * 256.0F / 360.0F))), dimensionId);
                ((IEDGMinecraftServer) server).getNPCPlayerList().broadcastAll(new ClientboundTeleportEntityPacket(instance), dimensionId);
                instance.entityData.set(DATA_PLAYER_MODE_CUSTOMISATION, (byte) 127);
                instance.getAbilities().flying = isflying;
                return instance;
            } catch (Exception e) {
                EroticDungeon.getLogger().error("Failed to create NPC", e);
            }
        } else EroticDungeon.getLogger().error("Failed to create NPC because server({}) is null!", dimensionId);
        return null;
    }

    @Override
    public void onEquipItem(@NotNull EquipmentSlot equipmentSlot, @NotNull ItemStack oldItem, @NotNull ItemStack newItem) {
        if (!this.isUsingItem()) {
            super.onEquipItem(equipmentSlot, oldItem, newItem);
        }

    }

    @Override
    protected void serverAiStep() {
        super.serverAiStep();
        int i = Objects.requireNonNull(this.level().getServer()).getTickCount() + this.getId();
        if (i % 2 != 0 && this.tickCount > 1) {
            this.level().getProfiler().push("targetSelector");
            this.targetSelector.tickRunningGoals(false);
            this.level().getProfiler().pop();
            this.level().getProfiler().push("goalSelector");
            this.goalSelector.tickRunningGoals(false);
            this.level().getProfiler().pop();
        } else {
            this.level().getProfiler().push("targetSelector");
            this.targetSelector.tick();
            this.level().getProfiler().pop();
            this.level().getProfiler().push("goalSelector");
            this.goalSelector.tick();
            this.level().getProfiler().pop();
        }
        this.level().getProfiler().push("navigation");
        this.navigation.tick();
        this.level().getProfiler().pop();
        this.level().getProfiler().push("mob tick");

        this.level().getProfiler().pop();
        this.level().getProfiler().push("controls");
        this.level().getProfiler().push("move");
        this.moveControl.tick();
        this.level().getProfiler().popPush("look");
        this.lookControl.tick();
        this.level().getProfiler().popPush("jump");
        this.jumpControl.tick();
        this.level().getProfiler().pop();
        this.level().getProfiler().pop();

    }

    @Override
    protected float tickHeadTurn(float yRot, float animStep) {
        this.bodyRotationControl.clientTick();
        return animStep;
    }

    @Override
    public void setSpeed(float speed) {
        super.setSpeed(speed);
        this.setZza(speed);
    }

    protected void updateControlFlags() {
        boolean flag = !(this.getControllingPassenger() instanceof Mob);
        boolean flag1 = !(this.getVehicle() instanceof Boat);
        this.goalSelector.setControlFlag(Goal.Flag.MOVE, flag);
        this.goalSelector.setControlFlag(Goal.Flag.JUMP, flag && flag1);
        this.goalSelector.setControlFlag(Goal.Flag.LOOK, flag);
    }

    public void setZza(float amount) {
        this.zza = amount;
    }

    public void setYya(float amount) {
        this.yya = amount;
    }

    public void setXxa(float amount) {
        this.xxa = amount;
    }

    @Override
    public void kill() {
        this.kill(Messenger.s("Killed"));
    }

    public void kill(Component reason) {
        this.shakeOff();
        this.server.tell(new TickTask(this.server.getTickCount(), () -> {
            if (this.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer) {
                iedgMinecraftServer.getNPCPlayerList().delete(this);
            }
        }));
    }

    private void shakeOff() {
        if (this.getVehicle() instanceof Player) {
            this.stopRiding();
        }

        for (Entity passenger : this.getIndirectPassengers()) {
            if (passenger instanceof Player) {
                passenger.stopRiding();
            }
        }

    }

    @Override
    public void die(@NotNull DamageSource damageSource) {
        this.shakeOff();
        super.die(damageSource);
        this.setHealth(20.0F);
        this.foodData = new FoodData();
        this.kill(this.getCombatTracker().getDeathMessage());
    }

    @Override
    public void addAdditionalSaveData(@NotNull CompoundTag compound) {
        super.addAdditionalSaveData(compound);
        String npcName = getName().getString();
        String npcNameWithoutPrefix = npcName.substring(PREFIX.length());
        NBTWriter.of(compound).string("NpcName", npcNameWithoutPrefix).compound("NpcGameProfile", NbtUtils.writeGameProfile(new CompoundTag(), this.getGameProfile()));
    }

    @Override
    public void readAdditionalSaveData(@NotNull CompoundTag compound) {
        super.readAdditionalSaveData(compound);
    }

    @Override
    public @NotNull String getIpAddress() {
        return "127.0.0.1";
    }

    @Override
    public @NotNull Component getName() {

        return Component.literal(PREFIX).append(super.getName());
    }

    @Override
    public void tick() {
        if (this.getServer() != null && this.getServer().getTickCount() % 10 == 0) {
            this.connection.resetPosition();
            ((ServerLevel) this.level()).getChunkSource().move(this);
        }
        if (!this.level().isClientSide) {
            if (this.tickCount % 5 == 0) {
                this.updateControlFlags();
            }
        }
        try {
            super.tick();
            this.doTick();
        } catch (NullPointerException ignored) {
        }

    }

    @Override
    public Entity changeDimension(@NotNull ServerLevel level) {
        super.changeDimension(level);
        if (this.wonGame) {
            ServerboundClientCommandPacket packet = new ServerboundClientCommandPacket(ServerboundClientCommandPacket.Action.PERFORM_RESPAWN);
            this.connection.handleClientCommand(packet);
        }

        if (this.connection.player.isChangingDimension()) {
            this.connection.player.hasChangedDimension();
        }

        return this.connection.player;
    }

    @Override
    protected void checkFallDamage(double y, boolean onGround, @NotNull BlockState state, @NotNull BlockPos pos) {
        this.doCheckFallDamage(this.getX(), y, this.getZ(), onGround);
    }

    @Override
    public @NotNull Packet<ClientGamePacketListener> getAddEntityPacket() {
        ClientboundAddPlayerPacket clientboundAddPlayerPacket = new ClientboundAddPlayerPacket(this);
        clientboundAddPlayerPacket.playerId = getUUID();
        return clientboundAddPlayerPacket;
    }

    @Nullable
    @Override
    public LivingEntity getTarget() {
        return target;
    }

    public void setTarget(@Nullable LivingEntity entity) {
        this.target = entity;
    }

    public NPCLookControl getLookControl() {
        return lookControl;
    }

    public NPCMoveControl getMoveControl() {
        return moveControl;
    }

    public NPCJumpControl getJumpControl() {
        return jumpControl;
    }

    public NPCPathNavigation getNavigation() {
        return navigation;
    }


}
