/*
 * SPDX-FileCopyrightText: 2026 KOKA99CAB
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

package net.koka99cab.portals.world.feature;

import com.mojang.serialization.Codec;
import net.koka99cab.portals.PortalsMod;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import net.minecraft.world.level.storage.loot.LootTable;

public final class SkeletonRuinFeature extends Feature<NoneFeatureConfiguration> {
	private static final ResourceKey<LootTable> LOOT_TABLE = ResourceKey.create(Registries.LOOT_TABLE, PortalsMod.id("chests/skeleton_ruin"));
	private static final Direction[] HORIZONTAL_DIRECTIONS = new Direction[] {
			Direction.NORTH,
			Direction.EAST,
			Direction.SOUTH,
			Direction.WEST
	};

	public SkeletonRuinFeature(Codec<NoneFeatureConfiguration> codec) {
		super(codec);
	}

	@Override
	public boolean place(FeaturePlaceContext<NoneFeatureConfiguration> context) {
		WorldGenLevel level = context.level();
		RandomSource random = context.random();
		BlockPos origin = context.origin();
		int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, origin.getX(), origin.getZ());

		if (y <= level.getMinY() + 5 || y >= level.getMaxY() - 10) {
			return false;
		}

		BlockPos base = new BlockPos(origin.getX(), y, origin.getZ());

		if (level.getBlockState(base.below()).isAir()) {
			return false;
		}

		boolean placed = false;
		int radius = 13;
		int keepRadius = 6;

		for (int x = -radius; x <= radius; x++) {
			for (int z = -radius; z <= radius; z++) {
				if ((Math.abs(x) == radius && Math.abs(z) == radius && random.nextBoolean()) || random.nextInt(7) == 0) {
					continue;
				}

				placed |= placeBlock(level, base.offset(x, -1, z), foundationBlock(random));
			}
		}

		placed |= buildWall(level, random, base, -radius, radius, -radius, -radius);
		placed |= buildWall(level, random, base, -radius, radius, radius, radius);
		placed |= buildWall(level, random, base, -radius, -radius, -radius, radius);
		placed |= buildWall(level, random, base, radius, radius, -radius, radius);
		placed |= buildWall(level, random, base, -keepRadius, keepRadius, -keepRadius, -keepRadius);
		placed |= buildWall(level, random, base, -keepRadius, keepRadius, keepRadius, keepRadius);
		placed |= buildWall(level, random, base, -keepRadius, -keepRadius, -keepRadius, keepRadius);
		placed |= buildWall(level, random, base, keepRadius, keepRadius, -keepRadius, keepRadius);

		placed |= buildTower(level, random, base.offset(-radius, 0, -radius), 2, 11 + random.nextInt(10));
		placed |= buildTower(level, random, base.offset(radius, 0, -radius), 2, 9 + random.nextInt(10));
		placed |= buildTower(level, random, base.offset(-radius, 0, radius), 2, 9 + random.nextInt(10));
		placed |= buildTower(level, random, base.offset(radius, 0, radius), 2, 11 + random.nextInt(10));
		placed |= buildTower(level, random, base.offset(0, 0, 0), 3, 13 + random.nextInt(13));

		for (int i = 0; i < 130; i++) {
			int x = random.nextInt(radius * 2 + 1) - radius;
			int z = random.nextInt(radius * 2 + 1) - radius;

			if (Math.abs(x) == radius || Math.abs(z) == radius || random.nextInt(5) == 0) {
				continue;
			}

			placed |= placeBlock(level, base.offset(x, random.nextInt(5), z), random.nextBoolean() ? wallBlock(random) : foundationBlock(random));
		}

		if (random.nextBoolean()) {
			placed |= placeLootChest(level, random, base.offset(random.nextInt(keepRadius * 2 + 1) - keepRadius, 0, random.nextInt(keepRadius * 2 + 1) - keepRadius));
		}

		return placed;
	}

	private static boolean buildWall(WorldGenLevel level, RandomSource random, BlockPos base, int x1, int x2, int z1, int z2) {
		boolean placed = false;
		int length = Math.max(Math.abs(x2 - x1), Math.abs(z2 - z1));

		for (int i = 0; i <= length; i++) {
			int x = x1 == x2 ? x1 : x1 + Integer.signum(x2 - x1) * i;
			int z = z1 == z2 ? z1 : z1 + Integer.signum(z2 - z1) * i;
			int height = 5 + random.nextInt(7);

			for (int y = 0; y < height; y++) {
				if (y > 1 && random.nextInt(4) == 0 || y > 4 && random.nextInt(3) == 0) {
					continue;
				}

				placed |= placeBlock(level, base.offset(x, y, z), wallBlock(random));
			}
		}

		return placed;
	}

	private static boolean buildTower(WorldGenLevel level, RandomSource random, BlockPos base, int towerRadius, int height) {
		boolean placed = false;

		for (int y = 0; y < height; y++) {
			for (int x = -towerRadius; x <= towerRadius; x++) {
				for (int z = -towerRadius; z <= towerRadius; z++) {
					boolean shell = Math.abs(x) == towerRadius || Math.abs(z) == towerRadius;

					if (!shell || y > 2 && random.nextInt(5) == 0 || y > height / 2 && random.nextInt(4) == 0) {
						continue;
					}

					placed |= placeBlock(level, base.offset(x, y, z), wallBlock(random));
				}
			}
		}

		for (int x = -towerRadius; x <= towerRadius; x++) {
			for (int z = -towerRadius; z <= towerRadius; z++) {
				if (Math.abs(x) + Math.abs(z) < towerRadius + 1 && random.nextBoolean()) {
					continue;
				}

				placed |= placeBlock(level, base.offset(x, height, z), wallBlock(random));
			}
		}

		return placed;
	}

	private static boolean placeLootChest(WorldGenLevel level, RandomSource random, BlockPos pos) {
		if (!placeBlock(level, pos, Blocks.CHEST.defaultBlockState().setValue(ChestBlock.FACING, HORIZONTAL_DIRECTIONS[random.nextInt(HORIZONTAL_DIRECTIONS.length)]))) {
			return false;
		}

		BlockEntity blockEntity = level.getBlockEntity(pos);

		if (blockEntity instanceof ChestBlockEntity chest) {
			chest.setLootTable(LOOT_TABLE);
			chest.setLootTableSeed(random.nextLong());
		}

		return true;
	}

	private static BlockState foundationBlock(RandomSource random) {
		return switch (random.nextInt(8)) {
			case 0 -> Blocks.SOUL_SOIL.defaultBlockState();
			case 1 -> Blocks.SOUL_SAND.defaultBlockState();
			case 2 -> Blocks.GRAVEL.defaultBlockState();
			case 3 -> Blocks.TUFF.defaultBlockState();
			case 4 -> Blocks.SMOOTH_BASALT.defaultBlockState();
			default -> Blocks.BASALT.defaultBlockState();
		};
	}

	private static BlockState wallBlock(RandomSource random) {
		return switch (random.nextInt(10)) {
			case 0 -> Blocks.POLISHED_BASALT.defaultBlockState();
			case 1 -> Blocks.SMOOTH_BASALT.defaultBlockState();
			case 2 -> Blocks.BLACKSTONE.defaultBlockState();
			case 3 -> Blocks.POLISHED_BLACKSTONE.defaultBlockState();
			case 4 -> Blocks.POLISHED_BLACKSTONE_BRICKS.defaultBlockState();
			case 5 -> Blocks.CRACKED_POLISHED_BLACKSTONE_BRICKS.defaultBlockState();
			case 6 -> Blocks.COBBLED_DEEPSLATE.defaultBlockState();
			default -> Blocks.BASALT.defaultBlockState();
		};
	}

	private static boolean placeBlock(WorldGenLevel level, BlockPos pos, BlockState state) {
		if (pos.getY() <= level.getMinY() || pos.getY() >= level.getMaxY() || !level.ensureCanWrite(pos)) {
			return false;
		}

		level.setBlock(pos, state, 3);
		return true;
	}
}
