package fr.koka99cab.sanctuary.worldgen.feature;

import com.mojang.serialization.Codec;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.LakeFeature;

public class SanctuaryWaterLakeFeature extends LakeFeature {
	private static final int LAKE_WIDTH = 16;
	private static final int WATER_LEVEL_HEIGHT = 4;
	private static final int SUGAR_CANE_ATTEMPTS = 28;
	private static final int MAX_SUGAR_CANE_COLUMNS = 5;
	private static final Direction[] HORIZONTAL_DIRECTIONS = {
		Direction.NORTH,
		Direction.SOUTH,
		Direction.WEST,
		Direction.EAST
	};

	public SanctuaryWaterLakeFeature(Codec<Configuration> codec) {
		super(codec);
	}

	@Override
	public boolean place(FeaturePlaceContext<Configuration> context) {
		if (!SanctuaryWorldgenChecks.isSanctuaryGenerator(context)) {
			return false;
		}

		boolean placed = super.place(context);
		if (placed) {
			placeSugarCane(context.level(), context.origin(), context.random());
		}
		return placed;
	}

	private static void placeSugarCane(WorldGenLevel level, BlockPos origin, RandomSource random) {
		BlockPos lakeOrigin = origin.offset(-8, -4, -8);
		List<BlockPos> candidates = collectSugarCaneCandidates(level, lakeOrigin);
		int placedColumns = 0;
		int attempts = Math.min(SUGAR_CANE_ATTEMPTS, candidates.size());

		for (int attempt = 0; attempt < attempts && placedColumns < MAX_SUGAR_CANE_COLUMNS; attempt++) {
			int index = random.nextInt(candidates.size());
			BlockPos candidate = candidates.remove(index);
			if (random.nextFloat() < 0.55F && placeSugarCaneColumn(level, candidate, random)) {
				placedColumns++;
			}
		}
	}

	private static List<BlockPos> collectSugarCaneCandidates(WorldGenLevel level, BlockPos lakeOrigin) {
		List<BlockPos> candidates = new ArrayList<>();
		BlockPos.MutableBlockPos waterPos = new BlockPos.MutableBlockPos();
		BlockPos.MutableBlockPos groundPos = new BlockPos.MutableBlockPos();

		for (int x = 1; x < LAKE_WIDTH - 1; x++) {
			for (int z = 1; z < LAKE_WIDTH - 1; z++) {
				for (int y = 0; y < WATER_LEVEL_HEIGHT; y++) {
					waterPos.set(lakeOrigin.getX() + x, lakeOrigin.getY() + y, lakeOrigin.getZ() + z);
					if (!isOpenWater(level, waterPos)) {
						continue;
					}

					for (Direction direction : HORIZONTAL_DIRECTIONS) {
						groundPos.set(waterPos).move(direction);
						BlockPos canePos = groundPos.above();
						if (canPlaceSugarCane(level, canePos)) {
							candidates.add(canePos);
						}
					}
				}
			}
		}

		return candidates;
	}

	private static boolean isOpenWater(WorldGenLevel level, BlockPos pos) {
		if (pos.getY() < level.getMinY() || pos.getY() >= level.getMinY() + level.getHeight() - 1) {
			return false;
		}

		return level.getBlockState(pos).getFluidState().is(FluidTags.WATER)
			&& level.getBlockState(pos.above()).isAir();
	}

	private static boolean canPlaceSugarCane(WorldGenLevel level, BlockPos pos) {
		BlockState sugarCane = Blocks.SUGAR_CANE.defaultBlockState();
		return pos.getY() >= level.getMinY()
			&& pos.getY() < level.getMinY() + level.getHeight()
			&& level.getBlockState(pos).isAir()
			&& sugarCane.canSurvive(level, pos);
	}

	private static boolean placeSugarCaneColumn(WorldGenLevel level, BlockPos origin, RandomSource random) {
		BlockPos.MutableBlockPos mutable = origin.mutable();
		int height = 2 + random.nextInt(3);
		boolean placed = false;

		for (int y = 0; y < height; y++) {
			if (!canPlaceSugarCane(level, mutable)) {
				break;
			}

			level.setBlock(mutable, Blocks.SUGAR_CANE.defaultBlockState(), Block.UPDATE_CLIENTS);
			placed = true;
			mutable.move(Direction.UP);
		}

		return placed;
	}
}
