Documentation
use fmc::{
    bevy::math::DVec3,
    blocks::{BlockId, Blocks},
    items::{Item, ItemStack, Items},
    networking::Server,
    players::{Player, Target, Targets},
    prelude::*,
    protocol::messages,
    utils::Rng,
    world::{chunk::ChunkPosition, BlockUpdate, ChunkSubscriptions},
};

use super::{DroppedItem, ItemRegistry, ItemUses};

pub struct HoePlugin;
impl Plugin for HoePlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(Startup, register_hoes)
            .add_systems(Update, use_hoe.after(super::ItemUseSystems));
    }
}

fn register_hoes(
    mut commands: Commands,
    blocks: Res<Blocks>,
    items: Res<Items>,
    mut usable_items: ResMut<ItemRegistry>,
) {
    for hoe in ["wooden_hoe", "stone_hoe", "iron_hoe"] {
        usable_items.insert(
            items.get_id(hoe).unwrap(),
            commands
                .spawn((
                    ItemUses::default(),
                    HoeConfig {
                        dirt: blocks.get_id("dirt"),
                        grass: blocks.get_id("grass"),
                    },
                ))
                .id(),
        );
    }
}

#[derive(Component)]
struct HoeConfig {
    pub dirt: BlockId,
    pub grass: BlockId,
}

fn use_hoe(
    mut commands: Commands,
    net: Res<Server>,
    items: Res<Items>,
    chunk_subscriptions: Res<ChunkSubscriptions>,
    player_query: Query<&Targets, With<Player>>,
    mut hoe_uses: Query<(&mut ItemUses, &HoeConfig), Changed<ItemUses>>,
    mut block_update_writer: EventWriter<BlockUpdate>,
    mut rng: Local<Rng>,
) {
    let Ok((mut uses, config)) = hoe_uses.get_single_mut() else {
        return;
    };

    for player_entity in uses.read() {
        let targets = player_query.get(player_entity).unwrap();

        let Some(Target::Block {
            block_position,
            block_id,
            ..
        }) = targets
            .get_first_block(|block_id| *block_id == config.dirt || *block_id == config.grass)
        else {
            continue;
        };

        if *block_id == config.grass {
            let item_config = items.get_config_by_name("wheat_seeds").unwrap();
            let item_stack = ItemStack::new(item_config, 1);
            commands.spawn((
                DroppedItem::new(item_stack),
                Transform::from_translation(block_position.as_dvec3() + DVec3::new(0.5, 1.1, 0.5)),
            ));
        }

        let blocks = Blocks::get();
        let soil_id = blocks.get_id("soil");
        let block_config = blocks.get_config(&soil_id);

        let chunk_position = ChunkPosition::from(*block_position);
        if let Some(subscribers) = chunk_subscriptions.get_subscribers(&chunk_position) {
            let position = block_position.as_dvec3() + DVec3::splat(0.5);

            if let Some(place_sound) = block_config.sound.place(&mut rng) {
                net.send_many(
                    subscribers,
                    messages::Sound {
                        position: Some(position),
                        volume: 1.0,
                        speed: 1.0,
                        sound: place_sound.to_owned(),
                    },
                )
            }
        }

        block_update_writer.send(BlockUpdate::Replace {
            position: *block_position,
            block_id: soil_id,
            block_state: None,
            block_data: None,
        });
    }
}