package com.tiedup.remake.v2.furniture.client;

import java.io.InputStream;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;

/**
 * Lazy-loading cache for parsed multi-armature furniture GLB data.
 *
 * <p>Loads .glb files via Minecraft's ResourceManager on first access and parses them
 * with {@link FurnitureGlbParser}. Thread-safe via {@link ConcurrentHashMap}.
 *
 * <p>Cache values are {@link Optional}: empty means a previous load/parse failed
 * and no retry should be attempted until {@link #clear()} is called. Previously
 * a private {@code FAILED_SENTINEL} instance of {@code FurnitureGltfData} with
 * a {@code null} furniture mesh was used for this purpose; identity-equality on
 * that sentinel was brittle and let future callers observe a half-constructed
 * data object.</p>
 *
 * <p>Call {@link #clear()} on resource reload (e.g., F3+T) to invalidate stale entries.
 *
 * <p>This class is client-only and must never be referenced from server code.
 */
@OnlyIn(Dist.CLIENT)
public final class FurnitureGltfCache {

    private static final Logger LOGGER = LogManager.getLogger("FurnitureGltf");

    private static final Map<
        ResourceLocation,
        Optional<FurnitureGltfData>
    > CACHE = new ConcurrentHashMap<>();

    private FurnitureGltfCache() {}

    /**
     * Get parsed furniture GLB data for a resource, loading and parsing on first access.
     *
     * @param modelLocation resource location of the .glb file
     *                      (e.g., {@code tiedup:models/furniture/wooden_stocks.glb})
     * @return parsed {@link FurnitureGltfData}, or {@code null} if loading/parsing failed
     *         (persistent until {@link #clear()} is called)
     */
    @Nullable
    public static FurnitureGltfData get(ResourceLocation modelLocation) {
        return CACHE.computeIfAbsent(
            modelLocation,
            FurnitureGltfCache::load
        ).orElse(null);
    }

    /**
     * Load and parse a furniture GLB from the resource manager. Returns an
     * empty {@link Optional} on any failure — the failure is cached so the
     * parser isn't invoked again for the same resource until {@link #clear()}.
     */
    private static Optional<FurnitureGltfData> load(ResourceLocation loc) {
        try {
            Resource resource = Minecraft.getInstance()
                .getResourceManager()
                .getResource(loc)
                .orElse(null);
            if (resource == null) {
                LOGGER.error("[FurnitureGltf] Resource not found: {}", loc);
                return Optional.empty();
            }

            try (InputStream is = resource.open()) {
                FurnitureGltfData data = FurnitureGlbParser.parse(
                    is,
                    loc.toString()
                );
                LOGGER.debug("[FurnitureGltf] Cached: {}", loc);
                return Optional.of(data);
            }
        } catch (Exception e) {
            LOGGER.error(
                "[FurnitureGltf] Failed to load furniture GLB: {}",
                loc,
                e
            );
            return Optional.empty();
        }
    }

    /**
     * Clear all cached data. Call on resource reload (F3+T) or dimension change.
     */
    public static void clear() {
        int size = CACHE.size();
        CACHE.clear();
        if (size > 0) {
            LOGGER.info("[FurnitureGltf] Cache cleared ({} entries)", size);
        }
    }
}
