use pumpkin_data::{Block, BlockId, block_properties::is_air, tag};
use pumpkin_util::{
    math::{position::BlockPos, vector3::Vector3},
    random::{RandomGenerator, RandomImpl},
};

use crate::generation::proto_chunk::GenerationCache;

pub struct IceSpikeFeature;

impl IceSpikeFeature {
    pub fn generate<T: GenerationCache>(
        chunk: &mut T,
        _min_y: i8,
        _height: u16,
        _feature: pumpkin_data::placed_feature::PlacedFeature,
        random: &mut RandomGenerator,
        pos: BlockPos,
    ) -> bool {
        let mut origin = pos.0;

        // Walk down while block is air, staying above minY + 2
        while chunk.is_air(&origin) && origin.y > chunk.bottom_y() as i32 + 2 {
            origin.y -= 1;
        }

        // Only place on snow blocks
        let block_id = GenerationCache::get_block_state(chunk, &origin).to_block_id();
        if block_id != Block::SNOW_BLOCK.id {
            return false;
        }

        origin.y += random.next_bounded_i32(4);
        let height = random.next_bounded_i32(4) + 7;
        let width = height / 4 + random.next_bounded_i32(2);

        // Occasionally create extra-tall spires
        if width > 1 && random.next_bounded_i32(60) == 0 {
            origin.y += 10 + random.next_bounded_i32(30);
        }

        for y_off in 0..height {
            let scale = (1.0f32 - y_off as f32 / height as f32) * width as f32;
            let new_width = scale.ceil() as i32;

            for xo in -new_width..=new_width {
                let dx = xo.abs() as f32 - 0.25f32;

                for zo in -new_width..=new_width {
                    let dz = zo.abs() as f32 - 0.25f32;

                    let in_sphere = (xo == 0 && zo == 0) || (dx * dx + dz * dz <= scale * scale);
                    let edge_ok = (xo != -new_width
                        && xo != new_width
                        && zo != -new_width
                        && zo != new_width)
                        || (random.next_f32() <= 0.75f32);

                    if in_sphere && edge_ok {
                        // Place packed ice upward
                        let place_pos = origin.add(&Vector3::new(xo, y_off, zo));
                        let state_id = GenerationCache::get_block_state(chunk, &place_pos);
                        let bid = state_id.to_block_id();
                        if is_air(state_id)
                            || bid.has_tag(tag::Block::MINECRAFT_DIRT)
                            || bid == BlockId::SNOW_BLOCK
                            || bid == BlockId::ICE
                        {
                            chunk.set_block_state(&place_pos, Block::PACKED_ICE.default_state);
                        }

                        // Mirror downward
                        if y_off != 0 && new_width > 1 {
                            let neg_pos = origin.add(&Vector3::new(xo, -y_off, zo));
                            let state_id2 = GenerationCache::get_block_state(chunk, &neg_pos);
                            let bid2 = state_id2.to_block_id();
                            if is_air(state_id2)
                                || bid2.has_tag(tag::Block::MINECRAFT_DIRT)
                                || bid2 == BlockId::SNOW_BLOCK
                                || bid2 == BlockId::ICE
                            {
                                chunk.set_block_state(&neg_pos, Block::PACKED_ICE.default_state);
                            }
                        }
                    }
                }
            }
        }

        // Build packed-ice roots downward from the spike base
        let pillar_width = (width - 1).clamp(0, 1);

        for xo in -pillar_width..=pillar_width {
            for zo in -pillar_width..=pillar_width {
                let mut ice_block = origin.add(&Vector3::new(xo, -1, zo));
                // Corner positions get a short random run; others run 50 blocks
                let mut run_length: i32 = if xo.abs() == 1 && zo.abs() == 1 {
                    random.next_bounded_i32(5)
                } else {
                    50
                };

                while ice_block.y > 50 {
                    let state_id = GenerationCache::get_block_state(chunk, &ice_block);
                    let bid = state_id.to_block_id();
                    if !is_air(state_id)
                        && !bid.has_tag(tag::Block::MINECRAFT_DIRT)
                        && bid != BlockId::SNOW_BLOCK
                        && bid != BlockId::ICE
                        && bid != BlockId::PACKED_ICE
                    {
                        break;
                    }

                    chunk.set_block_state(&ice_block, Block::PACKED_ICE.default_state);
                    ice_block.y -= 1;
                    run_length -= 1;
                    if run_length <= 0 {
                        ice_block.y -= random.next_bounded_i32(5) + 1;
                        run_length = random.next_bounded_i32(5);
                    }
                }
            }
        }

        true
    }
}
