package net.mcreator.customoregen.worldgen;

import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.neoforged.neoforge.common.world.BiomeModifier;
import net.neoforged.neoforge.common.world.ModifiableBiomeInfo;

import net.mcreator.customoregen.config.ConfigHelper;

import java.util.List;

/**
 * A {@link BiomeModifier} that adds {@link PlacedFeature}s to biomes only when
 * the matching config feature toggle is enabled.
 *
 * <p>This is the only correct way to gate world generation by a runtime
 * {@code ModConfig} value in NeoForge: data-driven biome modifiers are loaded
 * at bootstrap (before the config exists), but {@link #modify} runs at world
 * load, when {@link ConfigHelper#isFeatureEnabled(String)} is available.</p>
 *
 * <p>Referenced from biome modifier JSONs as
 * {@code "custom_ore_gen:config_gated_features"}.</p>
 */
public class ConfigGatedFeaturesModifier implements BiomeModifier {

	public static final MapCodec<ConfigGatedFeaturesModifier> CODEC = RecordCodecBuilder.mapCodec(
			inst -> inst.group(
					Biome.LIST_CODEC.fieldOf("biomes").forGetter(m -> m.biomes),
					GateGroup.CODEC.listOf().fieldOf("gate_groups").forGetter(m -> m.groups),
					GenerationStep.Decoration.CODEC.fieldOf("step").forGetter(m -> m.step)
			).apply(inst, inst.stable(ConfigGatedFeaturesModifier::new))
	);

	private final HolderSet<Biome> biomes;
	private final List<GateGroup> groups;
	private final GenerationStep.Decoration step;

	public ConfigGatedFeaturesModifier(HolderSet<Biome> biomes, List<GateGroup> groups, GenerationStep.Decoration step) {
		this.biomes = biomes;
		this.groups = groups;
		this.step = step;
	}

	@Override
	public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
		if (phase != Phase.ADD) {
			return;
		}
		if (!biomes.contains(biome)) {
			return;
		}
		for (GateGroup group : groups) {
			if (ConfigHelper.isFeatureEnabled(group.toggle())) {
				for (Holder<PlacedFeature> feature : group.features) {
					builder.getGenerationSettings().addFeature(step, feature);
				}
			}
		}
	}

	@Override
	public MapCodec<? extends BiomeModifier> codec() {
		return CODEC;
	}

	/** A named config toggle gating a set of placed features. */
	public record GateGroup(String toggle, HolderSet<PlacedFeature> features) {
		public static final Codec<GateGroup> CODEC = RecordCodecBuilder.create(
				inst -> inst.group(
						Codec.STRING.fieldOf("toggle").forGetter(GateGroup::toggle),
						PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(GateGroup::features)
				).apply(inst, inst.stable(GateGroup::new))
		);
	}
}
