use super::*;

pub(crate) fn apply_component_card_response(
    world: &mut World,
    entity: Entity,
    response: ComponentCardResponse,
) {
    let type_name = response_type_name(&response).unwrap_or_default();
    if type_name.is_empty() {
        return;
    }
    apply_component_card_response_for_type(world, entity, type_name, response);
}

pub(super) fn apply_component_card_response_for_type(
    world: &mut World,
    entity: Entity,
    type_name: &'static str,
    response: ComponentCardResponse,
) {
    if let Some(collapsed) = response.collapsed {
        let key = component_card_key(world, entity, type_name);
        let mut panel_state = world.resource_mut::<InspectorPanelState>();
        if collapsed {
            panel_state.collapsed_components.insert(key);
        } else {
            panel_state.collapsed_components.remove(&key);
        }
    }

    if let Some(active) = response.active {
        let mut states = world
            .get::<AuthoringComponentStates>(entity)
            .cloned()
            .unwrap_or_else(|| AuthoringComponentStates {
                states: world
                    .get::<InspectorOrder>(entity)
                    .map(|order| order.component_states.clone())
                    .unwrap_or_default(),
            });
        let component_id = world
            .resource::<EditorComponentRegistry>()
            .stable_id_for_type(type_name);
        states.set_component_active(component_id, active);
        let _ = crate::history::reflected_component_transaction(
            world,
            entity,
            "Set Component Active",
            "editor.component_states",
            "shared::components::AuthoringComponentStates",
            move |world, entity| {
                world.entity_mut(entity).insert(states);
                Ok(())
            },
        );
    }

    if response.move_up || response.move_down {
        let mut order = world
            .get::<InspectorOrder>(entity)
            .cloned()
            .unwrap_or_default();
        let present = present_component_type_names(world, entity);
        let offset = if response.move_up { -1 } else { 1 };
        let moved = world
            .resource::<EditorComponentRegistry>()
            .move_component(&mut order, type_name, offset, &present);
        if moved {
            set_inspector_order_with_history(world, entity, order);
        }
    }

    if response.copy {
        copy_component(world, entity, type_name);
    }
    if response.paste {
        paste_component(world, entity, type_name);
    }
    if response.reset {
        reset_component(world, entity, type_name);
    }
    if response.remove {
        remove_registered_component(world, entity, type_name);
    }
}

pub(super) fn response_type_name(_response: &ComponentCardResponse) -> Option<&'static str> {
    Some(_response.type_name)
}

pub(super) fn component_card_key(world: &World, entity: Entity, type_name: &str) -> String {
    let actor_key = world
        .get::<shared::ActorId>(entity)
        .map(|id| id.0.as_str())
        .filter(|id| !id.trim().is_empty())
        .map(str::to_string)
        .unwrap_or_else(|| format!("{entity:?}"));
    format!("{actor_key}::{type_name}")
}

pub(super) fn present_component_type_names(world: &World, entity: Entity) -> Vec<&'static str> {
    if let Some(registry) = world.get_resource::<EditorComponentRegistry>() {
        return registry
            .descriptors
            .iter()
            .filter(|descriptor| {
                !descriptor.hidden
                    && (descriptor.type_name != COMPONENT_MATERIAL_DESC
                        || world.get::<BrushDesc>(entity).is_some())
                    && registry.component_present(world, entity, descriptor.type_name)
            })
            .map(|descriptor| descriptor.type_name)
            .collect();
    }
    let mut present = Vec::new();
    if world.get::<AnimationControllerDesc>(entity).is_some() {
        present.push(COMPONENT_ANIMATION_CONTROLLER_DESC);
    }
    if world.get::<StaticMeshRenderer>(entity).is_some() {
        present.push(COMPONENT_STATIC_MESH_RENDERER);
    }
    if world.get::<SkinnedMeshRenderer>(entity).is_some() {
        present.push(COMPONENT_SKINNED_MESH_RENDERER);
    }
    if world.get::<Primitive>(entity).is_some() {
        present.push(COMPONENT_PRIMITIVE);
    }
    if world.get::<BrushDesc>(entity).is_some() {
        present.push(COMPONENT_BRUSH_DESC);
    }
    if world.get::<MaterialDesc>(entity).is_some() && world.get::<BrushDesc>(entity).is_some() {
        present.push(COMPONENT_MATERIAL_DESC);
    }
    if world.get::<LightDesc>(entity).is_some() {
        present.push(COMPONENT_LIGHT_DESC);
    }
    if world.get::<AudioSourceDesc>(entity).is_some() {
        present.push(COMPONENT_AUDIO_SOURCE_DESC);
    }
    if world.get::<AudioListenerDesc>(entity).is_some() {
        present.push(COMPONENT_AUDIO_LISTENER_DESC);
    }
    if world.get::<RigidBodyDesc>(entity).is_some() {
        present.push(COMPONENT_RIGID_BODY_DESC);
    }
    if world.get::<ColliderDesc>(entity).is_some() {
        present.push(COMPONENT_COLLIDER_DESC);
    }
    if world.get::<PhysicsBody>(entity).is_some() {
        present.push(COMPONENT_PHYSICS_BODY);
    }
    if world.get::<PlayerSpawn>(entity).is_some() {
        present.push(COMPONENT_PLAYER_SPAWN);
    }
    if world.get::<WeaponSpawn>(entity).is_some() {
        present.push(COMPONENT_WEAPON_SPAWN);
    }
    if world.get::<TriggerVolume>(entity).is_some() {
        present.push(COMPONENT_TRIGGER_VOLUME);
    }
    if world.get::<TeamSpawn>(entity).is_some() {
        present.push(COMPONENT_TEAM_SPAWN);
    }
    if world.get::<ObjectiveMarker>(entity).is_some() {
        present.push(COMPONENT_OBJECTIVE_MARKER);
    }
    if world.get::<PrefabInstance>(entity).is_some() {
        present.push(COMPONENT_PREFAB_INSTANCE);
    }
    if world.get::<PostProcessVolumeDesc>(entity).is_some() {
        present.push(COMPONENT_POST_PROCESS_VOLUME);
    }
    if world.get::<ProjectSun>(entity).is_some() {
        present.push(COMPONENT_PROJECT_SUN);
    }
    if world.get::<NavigationBounds>(entity).is_some() {
        present.push(COMPONENT_NAVIGATION_BOUNDS);
    }
    if world.get::<NavigationObstacle>(entity).is_some() {
        present.push(COMPONENT_NAVIGATION_OBSTACLE);
    }
    if world.get::<NavigationArea>(entity).is_some() {
        present.push(COMPONENT_NAVIGATION_AREA);
    }
    if world.get::<NavigationLink>(entity).is_some() {
        present.push(COMPONENT_NAVIGATION_LINK);
    }
    present
}

pub(super) fn copy_component(world: &mut World, entity: Entity, type_name: &str) {
    let descriptor = world
        .resource::<EditorComponentRegistry>()
        .by_type_name(type_name)
        .cloned();
    if let Some(descriptor) = descriptor {
        if let Ok(Some(value)) = crate::history::capture_reflected_component(
            world,
            entity,
            descriptor.id,
            descriptor.type_name,
        ) {
            world.resource_mut::<InspectorClipboard>().component =
                Some(CopiedComponent::Reflected(value));
            return;
        }
    }
    let component = match type_name {
        COMPONENT_ANIMATION_CONTROLLER_DESC => world
            .get::<AnimationControllerDesc>(entity)
            .cloned()
            .map(CopiedComponent::AnimationControllerDesc),
        COMPONENT_PRIMITIVE => world
            .get::<Primitive>(entity)
            .cloned()
            .map(CopiedComponent::Primitive),
        COMPONENT_BRUSH_DESC => world
            .get::<BrushDesc>(entity)
            .cloned()
            .map(CopiedComponent::BrushDesc),
        COMPONENT_STATIC_MESH_RENDERER => world
            .get::<StaticMeshRenderer>(entity)
            .cloned()
            .map(CopiedComponent::StaticMeshRenderer),
        COMPONENT_MATERIAL_DESC => world
            .get::<MaterialDesc>(entity)
            .cloned()
            .map(CopiedComponent::MaterialDesc),
        COMPONENT_LIGHT_DESC => world
            .get::<LightDesc>(entity)
            .cloned()
            .map(CopiedComponent::LightDesc),
        COMPONENT_AUDIO_SOURCE_DESC => world
            .get::<AudioSourceDesc>(entity)
            .cloned()
            .map(CopiedComponent::AudioSourceDesc),
        COMPONENT_AUDIO_LISTENER_DESC => world
            .get::<AudioListenerDesc>(entity)
            .copied()
            .map(CopiedComponent::AudioListenerDesc),
        COMPONENT_RIGID_BODY_DESC => world
            .get::<RigidBodyDesc>(entity)
            .copied()
            .map(CopiedComponent::RigidBodyDesc),
        COMPONENT_COLLIDER_DESC => world
            .get::<ColliderDesc>(entity)
            .cloned()
            .map(CopiedComponent::ColliderDesc),
        COMPONENT_PHYSICS_BODY => world
            .get::<PhysicsBody>(entity)
            .cloned()
            .map(CopiedComponent::PhysicsBody),
        COMPONENT_WEAPON_SPAWN => world
            .get::<WeaponSpawn>(entity)
            .cloned()
            .map(CopiedComponent::WeaponSpawn),
        COMPONENT_TRIGGER_VOLUME => world
            .get::<TriggerVolume>(entity)
            .cloned()
            .map(CopiedComponent::TriggerVolume),
        COMPONENT_TEAM_SPAWN => world
            .get::<TeamSpawn>(entity)
            .cloned()
            .map(CopiedComponent::TeamSpawn),
        COMPONENT_OBJECTIVE_MARKER => world
            .get::<ObjectiveMarker>(entity)
            .cloned()
            .map(CopiedComponent::ObjectiveMarker),
        COMPONENT_POST_PROCESS_VOLUME => world
            .get::<PostProcessVolumeDesc>(entity)
            .cloned()
            .map(CopiedComponent::PostProcessVolume),
        COMPONENT_PREFAB_INSTANCE => world
            .get::<PrefabInstance>(entity)
            .cloned()
            .map(CopiedComponent::PrefabInstance),
        COMPONENT_NAVIGATION_BOUNDS => world
            .get::<NavigationBounds>(entity)
            .cloned()
            .map(CopiedComponent::NavigationBounds),
        COMPONENT_NAVIGATION_OBSTACLE => world
            .get::<NavigationObstacle>(entity)
            .cloned()
            .map(CopiedComponent::NavigationObstacle),
        COMPONENT_NAVIGATION_AREA => world
            .get::<NavigationArea>(entity)
            .cloned()
            .map(CopiedComponent::NavigationArea),
        COMPONENT_NAVIGATION_LINK => world
            .get::<NavigationLink>(entity)
            .cloned()
            .map(CopiedComponent::NavigationLink),
        _ => None,
    };
    if let Some(component) = component {
        world.resource_mut::<InspectorClipboard>().component = Some(component);
    }
}

pub(super) fn paste_component(world: &mut World, entity: Entity, type_name: &str) {
    let component = world
        .get_resource::<InspectorClipboard>()
        .and_then(|clipboard| clipboard.component.clone());
    if let Some(CopiedComponent::Reflected(value)) = component.as_ref() {
        if value.type_path == type_name {
            let value = value.clone();
            let component_id = value.component_id.clone();
            let _ = crate::history::reflected_component_transaction(
                world,
                entity,
                "Paste Component",
                &component_id,
                type_name,
                move |world, entity| {
                    crate::history::apply_reflected_component(
                        world,
                        entity,
                        type_name,
                        Some(&value),
                    )
                },
            );
        }
        return;
    }
    match component {
        Some(CopiedComponent::Reflected(_)) => {}
        Some(CopiedComponent::AnimationControllerDesc(value))
            if type_name == COMPONENT_ANIMATION_CONTROLLER_DESC =>
        {
            set_animation_controller_with_history(world, entity, value);
        }
        Some(CopiedComponent::Primitive(value)) if type_name == COMPONENT_PRIMITIVE => {
            set_primitive_with_history(world, entity, value);
        }
        Some(CopiedComponent::BrushDesc(value)) if type_name == COMPONENT_BRUSH_DESC => {
            set_brush_with_history(world, entity, value);
        }
        Some(CopiedComponent::StaticMeshRenderer(value))
            if type_name == COMPONENT_STATIC_MESH_RENDERER =>
        {
            set_static_mesh_renderer_with_history(world, entity, value);
        }
        Some(CopiedComponent::MaterialDesc(value)) if type_name == COMPONENT_MATERIAL_DESC => {
            set_material_with_history(world, entity, value);
        }
        Some(CopiedComponent::LightDesc(value)) if type_name == COMPONENT_LIGHT_DESC => {
            set_light_with_history(world, entity, value);
        }
        Some(CopiedComponent::AudioSourceDesc(value))
            if type_name == COMPONENT_AUDIO_SOURCE_DESC =>
        {
            set_audio_source_with_history(world, entity, value);
        }
        Some(CopiedComponent::AudioListenerDesc(value))
            if type_name == COMPONENT_AUDIO_LISTENER_DESC =>
        {
            set_audio_listener_with_history(world, entity, value);
        }
        Some(CopiedComponent::RigidBodyDesc(value)) if type_name == COMPONENT_RIGID_BODY_DESC => {
            set_rigid_body_with_history(world, entity, value);
        }
        Some(CopiedComponent::ColliderDesc(value)) if type_name == COMPONENT_COLLIDER_DESC => {
            set_collider_with_history(world, entity, value);
        }
        Some(CopiedComponent::PhysicsBody(value)) if type_name == COMPONENT_PHYSICS_BODY => {
            set_physics_with_history(world, entity, value);
        }
        Some(CopiedComponent::PostProcessVolume(value))
            if type_name == COMPONENT_POST_PROCESS_VOLUME =>
        {
            set_post_process_volume_with_history(world, entity, value);
        }
        Some(CopiedComponent::WeaponSpawn(value)) if type_name == COMPONENT_WEAPON_SPAWN => {
            insert_direct_component(world, entity, value);
        }
        Some(CopiedComponent::TriggerVolume(value)) if type_name == COMPONENT_TRIGGER_VOLUME => {
            insert_direct_component(world, entity, value);
        }
        Some(CopiedComponent::TeamSpawn(value)) if type_name == COMPONENT_TEAM_SPAWN => {
            insert_direct_component(world, entity, value);
        }
        Some(CopiedComponent::ObjectiveMarker(value))
            if type_name == COMPONENT_OBJECTIVE_MARKER =>
        {
            insert_direct_component(world, entity, value);
        }
        Some(CopiedComponent::PrefabInstance(value)) if type_name == COMPONENT_PREFAB_INSTANCE => {
            insert_direct_component(world, entity, value);
        }
        Some(CopiedComponent::NavigationBounds(mut value))
            if type_name == COMPONENT_NAVIGATION_BOUNDS =>
        {
            value.artifact_path = world
                .get::<NavigationBounds>(entity)
                .map(|bounds| bounds.artifact_path.clone())
                .unwrap_or_else(|| navigation_bounds_for_entity(world, entity).artifact_path);
            crate::history::set_navigation_with_history(
                world,
                entity,
                crate::history::NavigationComponentState {
                    bounds: Some(value),
                    ..Default::default()
                },
            );
        }
        Some(CopiedComponent::NavigationObstacle(value))
            if type_name == COMPONENT_NAVIGATION_OBSTACLE =>
        {
            crate::history::set_navigation_with_history(
                world,
                entity,
                crate::history::NavigationComponentState {
                    obstacle: Some(value),
                    ..Default::default()
                },
            );
        }
        Some(CopiedComponent::NavigationArea(value)) if type_name == COMPONENT_NAVIGATION_AREA => {
            crate::history::set_navigation_with_history(
                world,
                entity,
                crate::history::NavigationComponentState {
                    area: Some(value),
                    ..Default::default()
                },
            );
        }
        Some(CopiedComponent::NavigationLink(value)) if type_name == COMPONENT_NAVIGATION_LINK => {
            crate::history::set_navigation_with_history(
                world,
                entity,
                crate::history::NavigationComponentState {
                    link: Some(value),
                    ..Default::default()
                },
            );
        }
        _ => {}
    }
}

pub(super) fn reset_component(world: &mut World, entity: Entity, type_name: &str) {
    let descriptor = world
        .resource::<EditorComponentRegistry>()
        .by_type_name(type_name)
        .cloned();
    if let Some(descriptor) = descriptor {
        let component_id = descriptor.id;
        let type_path = descriptor.type_name;
        if type_path == COMPONENT_ANIMATION_CONTROLLER_DESC {
            let controller =
                crate::ui::animation_inspector::default_controller_for_actor(world, entity);
            let _ = crate::history::reflected_component_transaction(
                world,
                entity,
                "Reset Component",
                component_id,
                type_path,
                move |world, entity| {
                    world.entity_mut(entity).insert(controller);
                    Ok(())
                },
            );
        } else if type_path == COMPONENT_NAVIGATION_BOUNDS {
            let bounds = reset_navigation_bounds_for_entity(world, entity);
            let _ = crate::history::reflected_component_transaction(
                world,
                entity,
                "Reset Component",
                component_id,
                type_path,
                move |world, entity| {
                    world.entity_mut(entity).insert(bounds);
                    Ok(())
                },
            );
        } else {
            let _ = crate::history::reflected_component_transaction(
                world,
                entity,
                "Reset Component",
                component_id,
                type_path,
                move |world, entity| {
                    crate::history::apply_reflected_default(world, entity, type_path)
                },
            );
        }
        return;
    }
    match type_name {
        COMPONENT_ANIMATION_CONTROLLER_DESC => {
            let controller =
                crate::ui::animation_inspector::default_controller_for_actor(world, entity);
            set_animation_controller_with_history(world, entity, controller);
        }
        COMPONENT_PRIMITIVE => set_primitive_with_history(world, entity, Primitive::default()),
        COMPONENT_BRUSH_DESC => set_brush_with_history(world, entity, BrushDesc::default()),
        COMPONENT_STATIC_MESH_RENDERER => {
            set_static_mesh_renderer_with_history(world, entity, StaticMeshRenderer::default());
        }
        COMPONENT_MATERIAL_DESC => {
            set_material_with_history(world, entity, MaterialDesc::default())
        }
        COMPONENT_LIGHT_DESC => set_light_with_history(world, entity, LightDesc::default()),
        COMPONENT_AUDIO_SOURCE_DESC => {
            set_audio_source_with_history(world, entity, AudioSourceDesc::default())
        }
        COMPONENT_AUDIO_LISTENER_DESC => {
            set_audio_listener_with_history(world, entity, AudioListenerDesc::default())
        }
        COMPONENT_RIGID_BODY_DESC => {
            set_rigid_body_with_history(world, entity, RigidBodyDesc::default());
        }
        COMPONENT_COLLIDER_DESC => {
            set_collider_with_history(world, entity, ColliderDesc::default())
        }
        COMPONENT_PHYSICS_BODY => set_physics_with_history(world, entity, PhysicsBody::default()),
        COMPONENT_POST_PROCESS_VOLUME => {
            set_post_process_volume_with_history(world, entity, PostProcessVolumeDesc::default());
        }
        COMPONENT_NAVIGATION_BOUNDS => crate::history::set_navigation_with_history(
            world,
            entity,
            crate::history::NavigationComponentState {
                bounds: Some(reset_navigation_bounds_for_entity(world, entity)),
                ..Default::default()
            },
        ),
        COMPONENT_NAVIGATION_OBSTACLE => crate::history::set_navigation_with_history(
            world,
            entity,
            crate::history::NavigationComponentState {
                obstacle: Some(NavigationObstacle::default()),
                ..Default::default()
            },
        ),
        COMPONENT_NAVIGATION_AREA => crate::history::set_navigation_with_history(
            world,
            entity,
            crate::history::NavigationComponentState {
                area: Some(NavigationArea::default()),
                ..Default::default()
            },
        ),
        COMPONENT_NAVIGATION_LINK => crate::history::set_navigation_with_history(
            world,
            entity,
            crate::history::NavigationComponentState {
                link: Some(NavigationLink::default()),
                ..Default::default()
            },
        ),
        COMPONENT_WEAPON_SPAWN => insert_direct_component(
            world,
            entity,
            WeaponSpawn {
                weapon_id: "rifle".into(),
            },
        ),
        COMPONENT_TRIGGER_VOLUME => {
            insert_direct_component(world, entity, TriggerVolume::default())
        }
        COMPONENT_TEAM_SPAWN => insert_direct_component(world, entity, TeamSpawn { team_id: 0 }),
        COMPONENT_OBJECTIVE_MARKER => insert_direct_component(
            world,
            entity,
            ObjectiveMarker {
                objective_id: "objective".into(),
            },
        ),
        _ => {}
    }
}

pub(super) fn insert_direct_component<T: Component>(
    world: &mut World,
    entity: Entity,
    component: T,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(component);
    }
    if let Some(mut scene_io) = world.get_resource_mut::<crate::scene_io::SceneIo>() {
        scene_io.mark_dirty();
    }
}

pub(super) fn remove_registered_component(world: &mut World, entity: Entity, type_name: &str) {
    let descriptor = world
        .resource::<EditorComponentRegistry>()
        .by_type_name(type_name)
        .cloned();
    if let Some(descriptor) = descriptor {
        if !descriptor.removable {
            return;
        }
        let dependents = world
            .resource::<EditorComponentRegistry>()
            .present_dependents(world, entity, descriptor.id)
            .iter()
            .map(|dependent| dependent.display_name)
            .collect::<Vec<_>>();
        if !dependents.is_empty() {
            world
                .resource_mut::<crate::scene_io::SceneIo>()
                .set_status(format!(
                    "Remove blocked: required by {}. Remove dependent components first.",
                    dependents.join(", ")
                ));
            return;
        }
        let result = crate::history::reflected_component_transaction(
            world,
            entity,
            "Remove Component",
            descriptor.id,
            descriptor.type_name,
            |world, entity| {
                crate::history::apply_reflected_component(world, entity, descriptor.type_name, None)
            },
        );
        if result.is_ok() && type_name == COMPONENT_ANIMATION_CONTROLLER_DESC {
            crate::ui::animation_inspector::stop_preview_if_actor(world, entity);
        }
        return;
    }
    let Some(before) = crate::history::snapshot_entity(world, entity) else {
        return;
    };
    let removed_dedicated_audio_kind = matches!(
        (type_name, before.actor_kind),
        (COMPONENT_AUDIO_SOURCE_DESC, ActorKind::AudioSource)
            | (COMPONENT_AUDIO_LISTENER_DESC, ActorKind::AudioListener)
    );
    let removed_dedicated_navigation_kind = before.actor_kind == ActorKind::Navigation
        && matches!(
            type_name,
            COMPONENT_NAVIGATION_BOUNDS
                | COMPONENT_NAVIGATION_OBSTACLE
                | COMPONENT_NAVIGATION_AREA
                | COMPONENT_NAVIGATION_LINK
        );
    let removed_dedicated_skinned_kind =
        type_name == COMPONENT_SKINNED_MESH_RENDERER && before.actor_kind == ActorKind::SkinnedMesh;
    let removed = if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match type_name {
            COMPONENT_ANIMATION_CONTROLLER_DESC if before.animation_controller.is_some() => {
                entity_mut.remove::<AnimationControllerDesc>();
                true
            }
            COMPONENT_PRIMITIVE if before.primitive.is_some() => {
                entity_mut.remove::<Primitive>();
                true
            }
            COMPONENT_BRUSH_DESC if before.brush.is_some() => {
                entity_mut.remove::<BrushDesc>();
                true
            }
            COMPONENT_STATIC_MESH_RENDERER if before.static_mesh_renderer.is_some() => {
                entity_mut.remove::<StaticMeshRenderer>();
                true
            }
            COMPONENT_SKINNED_MESH_RENDERER if before.skinned_mesh_renderer.is_some() => {
                entity_mut.remove::<SkinnedMeshRenderer>();
                true
            }
            COMPONENT_MATERIAL_DESC if before.material.is_some() => {
                entity_mut.remove::<MaterialDesc>();
                true
            }
            COMPONENT_LIGHT_DESC if before.light.is_some() => {
                entity_mut.remove::<LightDesc>();
                true
            }
            COMPONENT_AUDIO_SOURCE_DESC if before.audio_source.is_some() => {
                entity_mut.remove::<AudioSourceDesc>();
                true
            }
            COMPONENT_AUDIO_LISTENER_DESC if before.audio_listener.is_some() => {
                entity_mut.remove::<AudioListenerDesc>();
                true
            }
            COMPONENT_RIGID_BODY_DESC if before.rigid_body.is_some() => {
                entity_mut.remove::<RigidBodyDesc>();
                true
            }
            COMPONENT_COLLIDER_DESC if before.collider.is_some() => {
                entity_mut.remove::<ColliderDesc>();
                true
            }
            COMPONENT_PHYSICS_BODY if before.physics.is_some() => {
                entity_mut.remove::<PhysicsBody>();
                true
            }
            COMPONENT_PLAYER_SPAWN if before.player_spawn => {
                entity_mut.remove::<PlayerSpawn>();
                true
            }
            COMPONENT_WEAPON_SPAWN if before.weapon_spawn.is_some() => {
                entity_mut.remove::<WeaponSpawn>();
                true
            }
            COMPONENT_TRIGGER_VOLUME if before.trigger_volume.is_some() => {
                entity_mut.remove::<TriggerVolume>();
                true
            }
            COMPONENT_TEAM_SPAWN if before.team_spawn.is_some() => {
                entity_mut.remove::<TeamSpawn>();
                true
            }
            COMPONENT_OBJECTIVE_MARKER if before.objective.is_some() => {
                entity_mut.remove::<ObjectiveMarker>();
                true
            }
            COMPONENT_PREFAB_INSTANCE if before.prefab_instance.is_some() => {
                entity_mut.remove::<PrefabInstance>();
                true
            }
            COMPONENT_POST_PROCESS_VOLUME if before.post_process_volume.is_some() => {
                entity_mut.remove::<PostProcessVolumeDesc>();
                true
            }
            COMPONENT_NAVIGATION_BOUNDS if before.navigation_bounds.is_some() => {
                entity_mut.remove::<NavigationBounds>();
                true
            }
            COMPONENT_NAVIGATION_OBSTACLE if before.navigation_obstacle.is_some() => {
                entity_mut.remove::<NavigationObstacle>();
                true
            }
            COMPONENT_NAVIGATION_AREA if before.navigation_area.is_some() => {
                entity_mut.remove::<NavigationArea>();
                true
            }
            COMPONENT_NAVIGATION_LINK if before.navigation_link.is_some() => {
                entity_mut.remove::<NavigationLink>();
                true
            }
            _ => false,
        }
    } else {
        false
    };
    if !removed {
        return;
    }
    if type_name == COMPONENT_ANIMATION_CONTROLLER_DESC {
        crate::ui::animation_inspector::stop_preview_if_actor(world, entity);
    }
    if removed_dedicated_audio_kind
        || removed_dedicated_navigation_kind
        || removed_dedicated_skinned_kind
    {
        let fallback = world
            .get_entity(entity)
            .ok()
            .and_then(infer_actor_kind)
            .unwrap_or(ActorKind::Empty);
        if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
            entity_mut.insert(fallback);
        }
    }
    if let Some(after) = crate::history::snapshot_entity(world, entity) {
        let snapshot = diff_added(&after, &before);
        crate::history::push_command(
            world,
            crate::history::EditorCommand::RemoveComponent { entity, snapshot },
        );
    }
}
