use crate::generation::proto_chunk::GenerationCache;
use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_util::{
    math::position::BlockPos,
    random::{RandomGenerator, RandomImpl},
};

pub struct UnderwaterMagmaFeature {
    /// vertical search limit from origin for a water column floor
    pub floor_search_range: i32,
    /// horizontal radius from the floor spot to sample
    pub placement_radius: i32,
    /// chance each inspected block turns into magma
    pub placement_probability: f32,
}

impl UnderwaterMagmaFeature {
    /// Find the Y coordinate of a solid floor beneath a water column at the origin's XZ.
    fn get_floor_y<T: GenerationCache>(&self, chunk: &T, origin: BlockPos) -> Option<i32> {
        let x = origin.0.x;
        let z = origin.0.z;

        // Drop down until water found
        for dy in 0..=self.floor_search_range {
            let check_y = origin.0.y - dy;
            let pos = BlockPos::new(x, check_y, z);
            let state_id = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();

            if state_id == Block::WATER {
                // Sink to first non-water block below the column
                let mut floor_y = check_y - 1;
                loop {
                    let floor_pos = BlockPos::new(x, floor_y, z);
                    let floor_id =
                        GenerationCache::get_block_state(chunk, &floor_pos.0).to_block_id();
                    if floor_id != Block::WATER {
                        return Some(floor_y);
                    }
                    if (check_y - floor_y) > self.floor_search_range {
                        break;
                    }
                    floor_y -= 1;
                }
            }
        }
        None
    }

    /// Check if a block can host magma
    fn is_valid_placement<T: GenerationCache>(chunk: &T, target: &BlockPos) -> bool {
        // Reject water/air or unsupported blocks
        let target_id = GenerationCache::get_block_state(chunk, &target.0).to_block_id();
        if target_id == Block::WATER || target_id == Block::AIR {
            return false;
        }

        // Below must be solid
        let below = target.offset(BlockDirection::Down.to_offset());
        let below_id = GenerationCache::get_block_state(chunk, &below.0).to_block_id();
        if below_id == Block::WATER || below_id == Block::AIR {
            return false;
        }

        // No open horizontal faces
        for dir in &BlockDirection::horizontal() {
            let neighbour = target.offset(dir.to_offset());
            let n_id = GenerationCache::get_block_state(chunk, &neighbour.0).to_block_id();
            if n_id == Block::WATER || n_id == Block::AIR {
                return false;
            }
        }

        true
    }

    #[allow(clippy::too_many_arguments)]
    pub fn generate<T: GenerationCache>(
        &self,
        chunk: &mut T,
        _min_y: i8,
        _height: u16,
        _feature_name: pumpkin_data::placed_feature::PlacedFeature,
        random: &mut RandomGenerator,
        pos: BlockPos,
    ) -> bool {
        // Locate floor; abort if none
        let Some(floor_y) = self.get_floor_y(chunk, pos) else {
            return false;
        };

        let floor_pos = BlockPos::new(pos.0.x, floor_y, pos.0.z);

        // Sample a cube around the floor, placing magma with probability and validity checks
        let mut placed = 0i32;
        let r = self.placement_radius;

        for dx in -r..=r {
            for dy in -r..=r {
                for dz in -r..=r {
                    if random.next_f32() >= self.placement_probability {
                        continue;
                    }

                    let target_pos =
                        BlockPos::new(floor_pos.0.x + dx, floor_pos.0.y + dy, floor_pos.0.z + dz);

                    if !Self::is_valid_placement(chunk, &target_pos) {
                        continue;
                    }

                    let magma_state = BlockState::from_id(Block::MAGMA_BLOCK.default_state.id);
                    chunk.set_block_state(&target_pos.0, magma_state);
                    placed += 1;
                }
            }
        }

        placed > 0
    }
}
