package com.tiedup.remake.worldgen;

import com.tiedup.remake.core.TiedUpMod;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder;

/**
 * Abstract base class for kidnapper structure types.
 *
 *
 * Subclasses:
 * - KidnapperCampStructure: Small, 2 tents
 * - KidnapperOutpostStructure: Medium, 2-3 cells around HQ
 * - KidnapperFortressStructure: Large, 4-6 cells with corridors
 *
 * All structures share terrain validation:
 * - Must be above sea level (Y >= 63)
 * - Terrain must be flat enough (configurable per type)
 * - No water at the surface
 */
public abstract class AbstractKidnapperStructure extends Structure {

    /** Standard Y offset for most kidnapper structures */
    protected static final int DEFAULT_Y_OFFSET = -2;

    /** Sea level - structures below this are rejected */
    private static final int SEA_LEVEL = 63;

    protected AbstractKidnapperStructure(StructureSettings settings) {
        super(settings);
    }

    /**
     * Get the radius (in blocks) to check for terrain flatness.
     * Larger structures need larger check areas.
     */
    protected abstract int getCheckRadius();

    /**
     * Get the maximum allowed height difference across sample points.
     * Stricter values reject more uneven terrain.
     */
    protected abstract int getMaxHeightDifference();

    /**
     * Get the Y offset for this structure type.
     * Override for structures with underground sections.
     */
    protected int getYOffset() {
        return DEFAULT_Y_OFFSET;
    }

    @Override
    protected Optional<GenerationStub> findGenerationPoint(
        GenerationContext context
    ) {
        ChunkPos chunkPos = context.chunkPos();

        int x = chunkPos.getMiddleBlockX();
        int z = chunkPos.getMiddleBlockZ();
        int y = context
            .chunkGenerator()
            .getFirstOccupiedHeight(
                x,
                z,
                Heightmap.Types.WORLD_SURFACE_WG,
                context.heightAccessor(),
                context.randomState()
            );

        BlockPos centerPos = new BlockPos(x, y, z);

        // Check 1: Not underground (above sea level)
        if (y < SEA_LEVEL) {
            TiedUpMod.LOGGER.debug(
                "[{}] Skipping at {} - below sea level (y={})",
                getClass().getSimpleName(),
                centerPos.toShortString(),
                y
            );
            return Optional.empty();
        }

        // Check 2: Terrain is flat enough
        if (!isTerrainFlat(context, centerPos)) {
            TiedUpMod.LOGGER.debug(
                "[{}] Skipping at {} - terrain not flat enough",
                getClass().getSimpleName(),
                centerPos.toShortString()
            );
            return Optional.empty();
        }

        // Check 3: No water at the surface
        if (hasWater(context, centerPos)) {
            TiedUpMod.LOGGER.debug(
                "[{}] Skipping at {} - water detected",
                getClass().getSimpleName(),
                centerPos.toShortString()
            );
            return Optional.empty();
        }

        return Optional.of(
            new GenerationStub(centerPos, builder -> {
                Rotation rotation = Rotation.getRandom(context.random());
                generatePieces(builder, context, centerPos, rotation);
            })
        );
    }

    /**
     * Check if the terrain is flat enough for this structure.
     * Samples heights at corners, edges, and midpoints of the footprint.
     */
    private boolean isTerrainFlat(
        GenerationContext context,
        BlockPos centerPos
    ) {
        int checkRadius = getCheckRadius();
        int centerY = centerPos.getY();
        int minY = centerY;
        int maxY = centerY;

        int[][] sampleOffsets = {
            { 0, 0 }, // Center
            { checkRadius, 0 }, // East
            { -checkRadius, 0 }, // West
            { 0, checkRadius }, // South
            { 0, -checkRadius }, // North
            { checkRadius, checkRadius }, // SE
            { checkRadius, -checkRadius }, // NE
            { -checkRadius, checkRadius }, // SW
            { -checkRadius, -checkRadius }, // NW
            { checkRadius / 2, checkRadius / 2 }, // Mid SE
            { checkRadius / 2, -checkRadius / 2 }, // Mid NE
            { -checkRadius / 2, checkRadius / 2 }, // Mid SW
            { -checkRadius / 2, -checkRadius / 2 }, // Mid NW
        };

        for (int[] offset : sampleOffsets) {
            int sampleX = centerPos.getX() + offset[0];
            int sampleZ = centerPos.getZ() + offset[1];

            int sampleY = context
                .chunkGenerator()
                .getFirstOccupiedHeight(
                    sampleX,
                    sampleZ,
                    Heightmap.Types.WORLD_SURFACE_WG,
                    context.heightAccessor(),
                    context.randomState()
                );

            minY = Math.min(minY, sampleY);
            maxY = Math.max(maxY, sampleY);
        }

        return (maxY - minY) <= getMaxHeightDifference();
    }

    /**
     * Check if there is water at the structure location.
     * Compares WORLD_SURFACE_WG with OCEAN_FLOOR_WG - if the difference > 1, there's water.
     * Uses 13 sample points (same as isTerrainFlat) to catch diagonal rivers and edges.
     */
    private boolean hasWater(GenerationContext context, BlockPos centerPos) {
        int checkRadius = getCheckRadius();

        // Same 13-point sampling as isTerrainFlat — catches diagonal rivers
        int[][] sampleOffsets = {
            { 0, 0 }, // Center
            { checkRadius, 0 }, // East
            { -checkRadius, 0 }, // West
            { 0, checkRadius }, // South
            { 0, -checkRadius }, // North
            { checkRadius, checkRadius }, // SE
            { checkRadius, -checkRadius }, // NE
            { -checkRadius, checkRadius }, // SW
            { -checkRadius, -checkRadius }, // NW
            { checkRadius / 2, checkRadius / 2 }, // Mid SE
            { checkRadius / 2, -checkRadius / 2 }, // Mid NE
            { -checkRadius / 2, checkRadius / 2 }, // Mid SW
            { -checkRadius / 2, -checkRadius / 2 }, // Mid NW
        };

        for (int[] offset : sampleOffsets) {
            int sampleX = centerPos.getX() + offset[0];
            int sampleZ = centerPos.getZ() + offset[1];

            int surfaceY = context
                .chunkGenerator()
                .getFirstOccupiedHeight(
                    sampleX,
                    sampleZ,
                    Heightmap.Types.WORLD_SURFACE_WG,
                    context.heightAccessor(),
                    context.randomState()
                );

            int oceanFloorY = context
                .chunkGenerator()
                .getFirstOccupiedHeight(
                    sampleX,
                    sampleZ,
                    Heightmap.Types.OCEAN_FLOOR_WG,
                    context.heightAccessor(),
                    context.randomState()
                );

            if (surfaceY - oceanFloorY > 1) {
                return true;
            }
        }

        return false;
    }

    /**
     * Generate the structure pieces.
     * Subclasses implement this to define their specific layout.
     */
    protected abstract void generatePieces(
        StructurePiecesBuilder builder,
        GenerationContext context,
        BlockPos centerPos,
        Rotation rotation
    );

    /**
     * Get the ground height at a specific position with Y offset applied.
     */
    protected int getGenerationHeight(GenerationContext context, int x, int z) {
        return (
            context
                .chunkGenerator()
                .getFirstOccupiedHeight(
                    x,
                    z,
                    Heightmap.Types.WORLD_SURFACE_WG,
                    context.heightAccessor(),
                    context.randomState()
                ) +
            getYOffset()
        );
    }

    /**
     * Rotate an offset position based on structure rotation.
     */
    protected int[] rotateOffset(int x, int z, Rotation rotation) {
        return switch (rotation) {
            case NONE -> new int[] { x, z };
            case CLOCKWISE_90 -> new int[] { -z, x };
            case CLOCKWISE_180 -> new int[] { -x, -z };
            case COUNTERCLOCKWISE_90 -> new int[] { z, -x };
        };
    }

    /**
     * Calculate position for a piece with rotated offset.
     */
    protected BlockPos calculatePiecePosition(
        GenerationContext context,
        BlockPos centerPos,
        int offsetX,
        int offsetZ,
        Rotation rotation
    ) {
        int[] rotatedOffset = rotateOffset(offsetX, offsetZ, rotation);
        int pieceX = centerPos.getX() + rotatedOffset[0];
        int pieceZ = centerPos.getZ() + rotatedOffset[1];
        int pieceY = getGenerationHeight(context, pieceX, pieceZ);
        return new BlockPos(pieceX, pieceY, pieceZ);
    }

    /**
     * Add surrounding pieces around a center position.
     * Common pattern for placing cells around a main building.
     */
    protected void addSurroundingPieces(
        StructurePiecesBuilder builder,
        GenerationContext context,
        BlockPos centerPos,
        Rotation rotation,
        int[][] offsets,
        String pieceName,
        int count
    ) {
        for (int i = 0; i < count && i < offsets.length; i++) {
            int offsetX = offsets[i][0];
            int offsetZ = offsets[i][1];

            BlockPos piecePos = calculatePiecePosition(
                context,
                centerPos,
                offsetX,
                offsetZ,
                rotation
            );
            builder.addPiece(
                new KidnapperCampPiece(
                    context.structureTemplateManager(),
                    ResourceLocation.fromNamespaceAndPath("tiedup", pieceName),
                    piecePos,
                    rotation
                )
            );
        }
    }
}
