package top.r3944realms.lib39.core.network.toClient;

import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.core.event.FabricCommonEventHandler;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.NBTEntitySyncData;
import top.r3944realms.lib39.core.sync.SyncData2Manager;

import java.util.Optional;

/**
 * The type Sync nbt lookup data entity s 2 c payload.
 */
public record SyncNBTLookupDataEntityS2CPayload(int entityId, Identifier id, CompoundTag data) implements CustomPacketPayload {
    /**
     * The constant SYNC_NBT_LOOKUP_PACKET_ID.
     */
    public static final Identifier SYNC_NBT_LOOKUP_PACKET_ID =
            Lib39.rl("sync_nbt_lookup_data_entity");
    /**
     * The constant TYPE.
     */
    public static final Type<SyncNBTLookupDataEntityS2CPayload> TYPE = new Type<>(SYNC_NBT_LOOKUP_PACKET_ID);

    /**
     * Stream codec for serializing/deserializing the packet.
     */
    public static final StreamCodec<RegistryFriendlyByteBuf, SyncNBTLookupDataEntityS2CPayload> STREAM_CODEC =
            StreamCodec.ofMember(SyncNBTLookupDataEntityS2CPayload::write, SyncNBTLookupDataEntityS2CPayload::new);


    /**
     * Instantiates a new Sync nbt data s 2 c pack.
     *
     * @param entityId the entity id
     * @param data     the data
     */
    public SyncNBTLookupDataEntityS2CPayload(int entityId, @NotNull NBTEntitySyncData data) {
        this(entityId, data.id(), data.serializeNBT());
    }

    /**
     * Instantiates from buffer.
     *
     * @param buf the buffer
     */
    public SyncNBTLookupDataEntityS2CPayload(RegistryFriendlyByteBuf buf) {
        this(buf.readInt(), buf.readIdentifier(), buf.readNbt());
    }

    /**
     * Write.
     *
     * @param friendlyByteBuf the friendly byte buf
     */
    public void write(@NotNull FriendlyByteBuf friendlyByteBuf) {
        friendlyByteBuf.writeInt(entityId);
        friendlyByteBuf.writeIdentifier(id);
        friendlyByteBuf.writeNbt(data);
    }


    /**
     * Receive.
     *
     * @param payload the packet
     * @param context the context
     */
    public static void handle(@NotNull SyncNBTLookupDataEntityS2CPayload payload, ClientPlayNetworking.Context context) {
        LocalPlayer localPlayer = context.player();
        Level level = localPlayer.level();
        Entity entity = level.getEntity(payload.entityId);

        if (entity != null) {
            Optional<SyncData2Manager.DataProvider<Entity, ISyncData<?>>> lookupOpt =
                    FabricCommonEventHandler
                            .getSyncData2Manager()
                            .getDataProvider(payload.id);

            lookupOpt.flatMap(dataProvider -> dataProvider.getData(entity))
                    .ifPresent(lookup -> {
                        if (lookup instanceof NBTEntitySyncData nbtLookup) {
                            CompoundTag current = nbtLookup.serializeNBT();
                            if (!current.equals(payload.data)) {
                                nbtLookup.deserializeNBT(payload.data);
                            }
                        } else {
                            Lib39.LOGGER.debug("Unhandled sync data: {}", payload.data);
                        }
                    });
        }
    }

    @Override
    public Type<? extends CustomPacketPayload> type() {
        return TYPE;
    }
}
