//! Maps authoring [`EditorVisibility`] to runtime [`Visibility`].

use crate::{EditorVisibility, LevelObject};
use bevy::prelude::*;

#[expect(
    clippy::type_complexity,
    reason = "the query mirrors Bevy's three visibility hierarchy components"
)]
pub fn ensure_level_object_visibility_hierarchy(
    mut commands: Commands,
    query: Query<
        (
            Entity,
            &EditorVisibility,
            Option<&Visibility>,
            Option<&InheritedVisibility>,
            Option<&ViewVisibility>,
        ),
        With<LevelObject>,
    >,
) {
    for (entity, editor, visibility, inherited_visibility, view_visibility) in &query {
        let mut entity_commands = commands.entity(entity);
        if visibility.is_none() {
            entity_commands.insert(visibility_from_editor(*editor));
        }
        if inherited_visibility.is_none() {
            entity_commands.insert(InheritedVisibility::default());
        }
        if view_visibility.is_none() {
            entity_commands.insert(ViewVisibility::default());
        }
    }
}

#[expect(
    clippy::type_complexity,
    reason = "the query expresses Bevy change detection at the system boundary"
)]
pub fn sync_editor_visibility(
    mut query: Query<
        (&EditorVisibility, &mut Visibility),
        (
            With<LevelObject>,
            Or<(Changed<EditorVisibility>, Added<EditorVisibility>)>,
        ),
    >,
) {
    for (editor, mut visibility) in query.iter_mut() {
        *visibility = visibility_from_editor(*editor);
    }
}

#[expect(
    clippy::type_complexity,
    reason = "the query expresses Bevy spawn detection at the system boundary"
)]
pub fn init_editor_visibility_on_spawn(
    mut query: Query<
        (&EditorVisibility, &mut Visibility),
        (
            With<LevelObject>,
            Added<LevelObject>,
            With<EditorVisibility>,
        ),
    >,
) {
    for (editor, mut visibility) in query.iter_mut() {
        *visibility = visibility_from_editor(*editor);
    }
}

pub fn visibility_from_editor(editor: EditorVisibility) -> Visibility {
    if editor.visible {
        Visibility::Visible
    } else {
        Visibility::Hidden
    }
}

#[cfg(test)]
mod tests {
    use super::ensure_level_object_visibility_hierarchy;
    use crate::{EditorVisibility, LevelObject};
    use bevy::prelude::*;

    #[test]
    fn level_object_visibility_hierarchy_is_inserted_for_generated_children() {
        let mut app = App::new();
        app.add_systems(Update, ensure_level_object_visibility_hierarchy);

        let entity = app
            .world_mut()
            .spawn((LevelObject, EditorVisibility { visible: false }))
            .id();

        app.update();

        let world = app.world();
        assert_eq!(world.get::<Visibility>(entity), Some(&Visibility::Hidden));
        assert!(world.get::<InheritedVisibility>(entity).is_some());
        assert!(world.get::<ViewVisibility>(entity).is_some());
    }
}
