package top.r3944realms.lib39.content.block.blockentity;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.authlib.GameProfile;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.Services;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import top.r3944realms.lib39.core.register.Lib39BlockEntities;

import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BooleanSupplier;

/**
 * The type Doll block entity.
 * 参考 SkullBlockEntity 实现，添加缓存机制
 */
public class DollBlockEntity extends BlockEntity {

    private static final String TAG_PROFILE = "profile";

    // 静态缓存（与 SkullBlockEntity 共享或独立）
    @Nullable
    private static Executor mainThreadExecutor;
    @Nullable
    private static LoadingCache<String, CompletableFuture<Optional<GameProfile>>> profileCacheByName;
    @Nullable
    private static LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> profileCacheById;

    public static final Executor CHECKED_MAIN_THREAD_EXECUTOR = (runnable) -> {
        Executor executor = mainThreadExecutor;
        if (executor != null) {
            executor.execute(runnable);
        }
    };

    @Nullable
    private ResolvableProfile owner;

    public DollBlockEntity(BlockPos pos, BlockState blockState) {
        super(Lib39BlockEntities.DOLL_BLOCK_ENTITY.get(), pos, blockState);
    }

    /**
     * 初始化缓存（在 Mod 初始化或服务器启动时调用）
     */
    public static void setup(final Services services, Executor executor) {
        mainThreadExecutor = executor;
        final BooleanSupplier cacheUninitialized = () -> profileCacheById == null;

        profileCacheByName = CacheBuilder.newBuilder()
                .expireAfterAccess(Duration.ofMinutes(10L))
                .maximumSize(256L)
                .build(new CacheLoader<>() {
                    @Override
                    public CompletableFuture<Optional<GameProfile>> load(String username) {
                        return fetchProfileByName(username, services);
                    }
                });

        profileCacheById = CacheBuilder.newBuilder()
                .expireAfterAccess(Duration.ofMinutes(10L))
                .maximumSize(256L)
                .build(new CacheLoader<>() {
                    @Override
                    public CompletableFuture<Optional<GameProfile>> load(UUID uuid) {
                        return fetchProfileById(uuid, services, cacheUninitialized);
                    }
                });
    }

    private static CompletableFuture<Optional<GameProfile>> fetchProfileByName(String name, Services services) {
        return services.profileCache().getAsync(name).thenCompose(optional -> {
            LoadingCache<UUID, CompletableFuture<Optional<GameProfile>>> cache = profileCacheById;
            if (cache != null && optional.isPresent()) {
                return cache.getUnchecked(optional.get().getId())
                        .thenApply(fullProfile -> fullProfile.or(() -> optional));
            }
            return CompletableFuture.completedFuture(Optional.empty());
        });
    }

    private static CompletableFuture<Optional<GameProfile>> fetchProfileById(UUID uuid, Services services, BooleanSupplier cacheUninitialized) {
        return CompletableFuture.supplyAsync(() -> {
            if (cacheUninitialized.getAsBoolean()) {
                return Optional.empty();
            }
            var result = services.sessionService().fetchProfile(uuid, true);
            return Optional.ofNullable(result).map(com.mojang.authlib.yggdrasil.ProfileResult::profile);
        }, Util.backgroundExecutor());
    }

    /**
     * 公开的静态方法：异步获取 GameProfile（复用 SkullBlockEntity 的缓存或使用自己的）
     */
    public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(String name) {
        if (profileCacheByName != null && name != null && !name.isEmpty()) {
            return profileCacheByName.getUnchecked(name);
        }
        // 回退到 SkullBlockEntity 的缓存
        return SkullBlockEntity.fetchGameProfile(name);
    }

    public static CompletableFuture<Optional<GameProfile>> fetchGameProfile(UUID uuid) {
        if (profileCacheById != null && uuid != null) {
            return profileCacheById.getUnchecked(uuid);
        }
        return SkullBlockEntity.fetchGameProfile(uuid);
    }

    public static void clearCache() {
        mainThreadExecutor = null;
        profileCacheByName = null;
        profileCacheById = null;
    }

    @Nullable
    public ResolvableProfile getOwnerProfile() {
        return this.owner;
    }

    @Override
    public ClientboundBlockEntityDataPacket getUpdatePacket() {
        return ClientboundBlockEntityDataPacket.create(this);
    }

    @Override
    public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
        CompoundTag tag = super.getUpdateTag(registries);
        saveAdditional(tag, registries);
        return tag;
    }

    public void setOwner(@Nullable ResolvableProfile owner) {
        synchronized (this) {
            this.owner = owner;
        }
        this.updateOwnerProfile();
    }

    public void setOwner(@Nullable String ownerName) {
        if (ownerName != null && !ownerName.isEmpty()) {
            setOwner(new ResolvableProfile(new GameProfile(Util.NIL_UUID, ownerName)));
        }
    }

    private void updateOwnerProfile() {
        if (this.owner != null && !this.owner.isResolved()) {
            this.owner.resolve().thenAcceptAsync(resolved -> {
                this.owner = resolved;
                this.setChanged();
                // 通知客户端更新
                if (level != null && !level.isClientSide) {
                    level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
                }
            }, CHECKED_MAIN_THREAD_EXECUTOR);
        } else {
            this.setChanged();
            if (level != null && !level.isClientSide && this.owner != null) {
                level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3);
            }
        }
    }

    @Override
    protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
        super.saveAdditional(tag, registries);
        if (this.owner != null) {
            tag.put(TAG_PROFILE, ResolvableProfile.CODEC.encodeStart(NbtOps.INSTANCE, this.owner).getOrThrow());
        }
    }

    @Override
    protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
        super.loadAdditional(tag, registries);
        if (tag.contains(TAG_PROFILE)) {
            ResolvableProfile.CODEC.parse(NbtOps.INSTANCE, tag.get(TAG_PROFILE))
                    .resultOrPartial(error -> {})
                    .ifPresent(this::setOwner);
        }
    }
}