use std::collections::HashSet;

use bevy::ecs::reflect::ReflectComponent;
use bevy::prelude::*;
use bevy::reflect::serde::{TypedReflectDeserializer, TypedReflectSerializer};
use bevy::reflect::std_traits::ReflectDefault;
use bevy::world_serialization::WorldInstance;
use serde::de::DeserializeSeed;
use shared::{
    infer_actor_kind, ActorId, ActorKind, ActorName, AnimationControllerDesc, AudioListenerDesc,
    AudioSourceDesc, AuthoringComponentStates, BrushDesc, ColliderDesc, EditorVisibility,
    HierarchySiblingIndex, HydratedPrefabMember, HydratedPrefabReady, InspectorOrder, LevelObject,
    LightDesc, MaterialDesc, MaterialOverride, ModelRef, NavigationArea, NavigationBounds,
    NavigationLink, NavigationObstacle, ObjectiveMarker, PhysicsBody, PlayerSpawn,
    PostProcessVolumeDesc, PrefabHydrationBlocked, PrefabInstance, PrefabRef, Primitive,
    RigidBodyDesc, SceneComposition, SkinnedMeshRenderer, StaticMeshRenderer, TeamSpawn,
    TriggerVolume, WeaponSpawn,
};

use crate::scene_io::{SceneIo, SceneIoRequest};
use crate::selection::SelectedEntity;
use crate::state::scene_tools_active;
use crate::ui::hierarchy_ops::{
    apply_sibling_change, apply_sibling_change_new, hierarchy_drop_violation, next_sibling_index,
    reorder_entities_under_parent,
};
use crate::ui::selection_ops::is_mutable_level_object;
use crate::ui::{egui_captures_keyboard_from_world, UiState};

mod commands;
pub use commands::{
    ComponentDelta, ComponentTransaction, EditorCommand, EditorEntitySnapshot,
    NavigationComponentState, PrefabEditState, ReflectedComponentValue, SiblingChange,
};

#[derive(Resource, Debug, Default)]
pub struct EditorHistory {
    undo_stack: Vec<EditorCommand>,
    redo_stack: Vec<EditorCommand>,
    pub status: String,
}

impl EditorHistory {
    pub fn push(&mut self, command: EditorCommand) {
        let label = command.label();
        self.undo_stack.push(command);
        self.redo_stack.clear();
        self.status = format!("Undo: {label}");
    }

    pub fn can_undo(&self) -> bool {
        !self.undo_stack.is_empty()
    }

    pub fn can_redo(&self) -> bool {
        !self.redo_stack.is_empty()
    }

    pub fn undo_depth(&self) -> usize {
        self.undo_stack.len()
    }

    pub fn set_undo_status(&mut self, label: impl Into<String>) {
        self.status = format!("Undo: {}", label.into());
    }

    fn set_redo_status(&mut self, label: impl Into<String>) {
        self.status = format!("Redo: {}", label.into());
    }

    fn pop_undo(&mut self) -> Option<EditorCommand> {
        self.undo_stack.pop()
    }

    fn push_undo(&mut self, command: EditorCommand) {
        self.undo_stack.push(command);
    }

    fn pop_redo(&mut self) -> Option<EditorCommand> {
        self.redo_stack.pop()
    }

    fn push_redo(&mut self, command: EditorCommand) {
        self.redo_stack.push(command);
    }

    pub fn clear(&mut self) {
        self.undo_stack.clear();
        self.redo_stack.clear();
        self.status = "History cleared".to_string();
    }
}

#[derive(Resource, Debug, Default)]
struct TransformEditTracker {
    active: Option<(Entity, Transform, Vec<(Entity, Transform)>)>,
}

pub struct EditorHistoryPlugin;

impl Plugin for EditorHistoryPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<EditorHistory>()
            .init_resource::<TransformEditTracker>()
            .add_systems(
                Update,
                (handle_history_hotkeys, capture_gizmo_transform_edits)
                    .chain()
                    .run_if(scene_tools_active),
            );
    }
}

pub fn snapshot_entity(world: &World, entity: Entity) -> Option<EditorEntitySnapshot> {
    let entity_ref = world.get_entity(entity).ok()?;
    if !entity_ref.contains::<LevelObject>()
        || entity_ref.contains::<HydratedPrefabMember>()
        || entity_ref.contains::<crate::scene_io::ComposedSceneMember>()
    {
        return None;
    }

    let children = entity_ref
        .get::<Children>()
        .map(|children| {
            children
                .iter()
                .filter_map(|child| snapshot_entity(world, child))
                .collect()
        })
        .unwrap_or_default();

    let actor_kind = entity_ref
        .get::<ActorKind>()
        .copied()
        .or_else(|| infer_actor_kind(entity_ref))
        .unwrap_or(ActorKind::Empty);

    Some(EditorEntitySnapshot {
        actor_id: entity_ref.get::<ActorId>().cloned(),
        actor_kind,
        actor_name: entity_ref.get::<ActorName>().cloned(),
        name: entity_ref
            .get::<Name>()
            .map(|name| name.as_str().to_string()),
        transform: entity_ref.get::<Transform>().copied().unwrap_or_default(),
        primitive: entity_ref.get::<Primitive>().cloned(),
        brush: entity_ref.get::<BrushDesc>().cloned(),
        static_mesh_renderer: entity_ref.get::<StaticMeshRenderer>().cloned(),
        skinned_mesh_renderer: entity_ref.get::<SkinnedMeshRenderer>().cloned(),
        material: entity_ref.get::<MaterialDesc>().cloned(),
        material_override: entity_ref.get::<MaterialOverride>().cloned(),
        rigid_body: entity_ref.get::<RigidBodyDesc>().cloned(),
        collider: entity_ref.get::<ColliderDesc>().cloned(),
        physics: entity_ref.get::<PhysicsBody>().cloned(),
        light: entity_ref.get::<LightDesc>().cloned(),
        animation_controller: entity_ref.get::<AnimationControllerDesc>().cloned(),
        audio_source: entity_ref.get::<AudioSourceDesc>().cloned(),
        audio_listener: entity_ref.get::<AudioListenerDesc>().copied(),
        player_spawn: entity_ref.contains::<PlayerSpawn>(),
        model: entity_ref.get::<ModelRef>().cloned(),
        prefab: entity_ref.get::<PrefabRef>().cloned(),
        prefab_instance: entity_ref.get::<PrefabInstance>().cloned(),
        weapon_spawn: entity_ref.get::<WeaponSpawn>().cloned(),
        trigger_volume: entity_ref.get::<TriggerVolume>().cloned(),
        post_process_volume: entity_ref.get::<PostProcessVolumeDesc>().cloned(),
        team_spawn: entity_ref.get::<TeamSpawn>().cloned(),
        objective: entity_ref.get::<ObjectiveMarker>().cloned(),
        navigation_bounds: entity_ref.get::<NavigationBounds>().cloned(),
        navigation_obstacle: entity_ref.get::<NavigationObstacle>().cloned(),
        navigation_area: entity_ref.get::<NavigationArea>().cloned(),
        navigation_link: entity_ref.get::<NavigationLink>().cloned(),
        hierarchy_sibling_index: entity_ref
            .get::<HierarchySiblingIndex>()
            .map(|index| index.0)
            .unwrap_or(0),
        editor_visibility: entity_ref
            .get::<EditorVisibility>()
            .copied()
            .unwrap_or_default(),
        inspector_order: entity_ref.get::<InspectorOrder>().cloned(),
        component_states: entity_ref.get::<AuthoringComponentStates>().cloned(),
        children,
    })
}

pub fn spawn_snapshot(world: &mut World, snapshot: &EditorEntitySnapshot) -> Entity {
    spawn_snapshot_with_parent(world, snapshot, None)
}

fn new_actor_id() -> ActorId {
    ActorId::new(uuid::Uuid::new_v4().to_string())
}

fn spawn_snapshot_with_parent(
    world: &mut World,
    snapshot: &EditorEntitySnapshot,
    parent: Option<Entity>,
) -> Entity {
    let mut entity_mut = world.spawn((LevelObject, snapshot.actor_kind, snapshot.transform));
    entity_mut.insert(
        snapshot
            .actor_id
            .clone()
            .filter(|id| !id.0.trim().is_empty())
            .unwrap_or_else(new_actor_id),
    );
    if let Some(actor_name) = &snapshot.actor_name {
        entity_mut.insert(actor_name.clone());
    }
    if let Some(name) = &snapshot.name {
        entity_mut.insert(Name::new(name.clone()));
    }
    if let Some(primitive) = &snapshot.primitive {
        entity_mut.insert(primitive.clone());
    }
    if let Some(brush) = &snapshot.brush {
        entity_mut.insert(brush.clone());
    }
    if let Some(renderer) = &snapshot.static_mesh_renderer {
        entity_mut.insert(renderer.clone());
    }
    if let Some(renderer) = &snapshot.skinned_mesh_renderer {
        entity_mut.insert(renderer.clone());
    }
    if let Some(material) = &snapshot.material {
        entity_mut.insert(material.clone());
    }
    if let Some(material_override) = &snapshot.material_override {
        entity_mut.insert(material_override.clone());
    }
    if let Some(rigid_body) = &snapshot.rigid_body {
        entity_mut.insert(*rigid_body);
    }
    if let Some(collider) = &snapshot.collider {
        entity_mut.insert(collider.clone());
    }
    if let Some(physics) = &snapshot.physics {
        entity_mut.insert(physics.clone());
    }
    if let Some(light) = &snapshot.light {
        entity_mut.insert(light.clone());
    }
    if let Some(controller) = &snapshot.animation_controller {
        entity_mut.insert(controller.clone());
    }
    if let Some(audio_source) = &snapshot.audio_source {
        entity_mut.insert(audio_source.clone());
    }
    if let Some(audio_listener) = snapshot.audio_listener {
        entity_mut.insert(audio_listener);
    }
    if snapshot.player_spawn {
        entity_mut.insert(PlayerSpawn);
    }
    if let Some(model) = &snapshot.model {
        entity_mut.insert(model.clone());
    }
    if let Some(prefab) = &snapshot.prefab {
        entity_mut.insert(prefab.clone());
    }
    if let Some(instance) = &snapshot.prefab_instance {
        entity_mut.insert(instance.clone());
    }
    if let Some(weapon) = &snapshot.weapon_spawn {
        entity_mut.insert(weapon.clone());
    }
    if let Some(trigger) = &snapshot.trigger_volume {
        entity_mut.insert(trigger.clone());
    }
    if let Some(pp) = &snapshot.post_process_volume {
        entity_mut.insert(pp.clone());
    }
    if let Some(team) = &snapshot.team_spawn {
        entity_mut.insert(team.clone());
    }
    if let Some(objective) = &snapshot.objective {
        entity_mut.insert(objective.clone());
    }
    if let Some(bounds) = &snapshot.navigation_bounds {
        entity_mut.insert(bounds.clone());
    }
    if let Some(obstacle) = &snapshot.navigation_obstacle {
        entity_mut.insert(obstacle.clone());
    }
    if let Some(area) = &snapshot.navigation_area {
        entity_mut.insert(area.clone());
    }
    if let Some(link) = &snapshot.navigation_link {
        entity_mut.insert(link.clone());
    }
    entity_mut.insert(HierarchySiblingIndex(snapshot.hierarchy_sibling_index));
    entity_mut.insert(snapshot.editor_visibility);
    if let Some(order) = &snapshot.inspector_order {
        entity_mut.insert(order.clone());
    }
    if let Some(states) = &snapshot.component_states {
        entity_mut.insert(states.clone());
    }
    if let Some(parent) = parent {
        entity_mut.insert(ChildOf(parent));
    }

    let entity = entity_mut.id();
    for child in &snapshot.children {
        spawn_snapshot_with_parent(world, child, Some(entity));
    }
    entity
}

pub fn clear_level_objects(world: &mut World) {
    let mut query = world.query_filtered::<Entity, With<LevelObject>>();
    let entities: Vec<Entity> = query.iter(world).collect();
    for entity in entities {
        if let Ok(entity_mut) = world.get_entity_mut(entity) {
            entity_mut.despawn();
        }
    }
}

pub fn spawn_with_history(world: &mut World, mut snapshot: EditorEntitySnapshot) -> Entity {
    assign_missing_actor_ids(&mut snapshot);
    snapshot.hierarchy_sibling_index = next_sibling_index(world, None);
    let entity = spawn_snapshot(world, &snapshot);
    push_history(
        world,
        EditorCommand::Spawn {
            snapshot,
            entity: Some(entity),
        },
    );
    select_one(world, entity);
    entity
}

pub fn spawn_many_with_history(
    world: &mut World,
    snapshots: impl IntoIterator<Item = EditorEntitySnapshot>,
) -> Vec<Entity> {
    let mut snapshots: Vec<EditorEntitySnapshot> = snapshots.into_iter().collect();
    if snapshots.is_empty() {
        return Vec::new();
    }
    for snapshot in &mut snapshots {
        assign_missing_actor_ids(snapshot);
    }
    for (sibling_index, snapshot) in (next_sibling_index(world, None)..).zip(snapshots.iter_mut()) {
        snapshot.hierarchy_sibling_index = sibling_index;
    }
    let entities: Vec<Entity> = snapshots
        .iter()
        .map(|snapshot| spawn_snapshot(world, snapshot))
        .collect();
    push_history(
        world,
        EditorCommand::SpawnMany {
            snapshots,
            entities: entities.clone(),
        },
    );
    select_many(world, &entities);
    entities
}

pub fn delete_entities_with_history(world: &mut World, entities: &[Entity]) {
    let mut deleted = Vec::new();
    for entity in entities {
        if !is_mutable_level_object(world, *entity) {
            continue;
        }
        if let Some(snapshot) = snapshot_entity(world, *entity) {
            deleted.push((snapshot, *entity));
        }
    }

    for (_, entity) in &deleted {
        if let Ok(entity_mut) = world.get_entity_mut(*entity) {
            entity_mut.despawn();
        }
    }

    for (snapshot, entity) in deleted {
        push_history(
            world,
            EditorCommand::Despawn {
                snapshot,
                entity: Some(entity),
            },
        );
    }
    clear_selection(world);
}

pub fn duplicate_entities_with_history(world: &mut World, entities: &[Entity]) {
    let snapshots: Vec<EditorEntitySnapshot> = entities
        .iter()
        .filter(|entity| is_mutable_level_object(world, **entity))
        .filter_map(|entity| snapshot_entity(world, *entity))
        .map(|mut snapshot| {
            assign_fresh_actor_ids(&mut snapshot);
            snapshot.transform.translation += Vec3::new(1.0, 0.0, 1.0);
            if let Some(name) = &mut snapshot.name {
                name.push_str(" Copy");
            }
            snapshot
        })
        .collect();

    if snapshots.is_empty() {
        return;
    }

    let entities: Vec<Entity> = snapshots
        .iter()
        .map(|snapshot| spawn_snapshot(world, snapshot))
        .collect();
    push_history(
        world,
        EditorCommand::Duplicate {
            snapshots,
            entities: entities.clone(),
        },
    );
    select_many(world, &entities);
}

fn assign_fresh_actor_ids(snapshot: &mut EditorEntitySnapshot) {
    let actor_id = new_actor_id();
    if let Some(bounds) = &mut snapshot.navigation_bounds {
        bounds.artifact_path = shared::navigation_artifact_path_for_actor(&actor_id.0);
    }
    snapshot.actor_id = Some(actor_id);
    for child in &mut snapshot.children {
        assign_fresh_actor_ids(child);
    }
}

fn assign_missing_actor_ids(snapshot: &mut EditorEntitySnapshot) {
    let existing_actor_id = snapshot
        .actor_id
        .clone()
        .filter(|id| !id.0.trim().is_empty());
    let assigned_new_id = existing_actor_id.is_none();
    let actor_id = existing_actor_id.unwrap_or_else(new_actor_id);
    if let Some(bounds) = &mut snapshot.navigation_bounds {
        if assigned_new_id
            || bounds.artifact_path == NavigationBounds::default().artifact_path
            || shared::navigation_generated_artifact_path(&bounds.artifact_path).is_err()
        {
            bounds.artifact_path = shared::navigation_artifact_path_for_actor(&actor_id.0);
        }
    }
    snapshot.actor_id = Some(actor_id);
    for child in &mut snapshot.children {
        assign_missing_actor_ids(child);
    }
}

pub fn rename_entity_with_history(world: &mut World, entity: Entity, new_name: String) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world
        .get::<Name>(entity)
        .map(|name| name.as_str().to_string())
        .unwrap_or_default();
    if old == new_name {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(Name::new(new_name.clone()));
    }
    push_history(
        world,
        EditorCommand::Rename {
            entity,
            old,
            new: new_name,
        },
    );
}

pub fn set_material_with_history(world: &mut World, entity: Entity, new: MaterialDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<MaterialDesc>(entity).cloned();
    if old.as_ref().is_some_and(|old| material_eq(old, &new)) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetMaterial { entity, old, new });
}

pub fn set_material_group_with_history(
    world: &mut World,
    changes: impl IntoIterator<Item = (Entity, MaterialDesc)>,
) {
    let mut entities = Vec::new();
    let mut olds = Vec::new();
    let mut news = Vec::new();
    for (entity, new) in changes {
        if !is_mutable_level_object(world, entity) {
            continue;
        }
        let old = world.get::<MaterialDesc>(entity).cloned();
        if old.as_ref().is_some_and(|old| material_eq(old, &new)) {
            continue;
        }
        if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
            entity_mut.insert(new.clone());
            entities.push(entity);
            olds.push(old);
            news.push(new);
        }
    }
    if !entities.is_empty() {
        push_history(
            world,
            EditorCommand::SetMaterialGroup {
                entities,
                olds,
                news,
            },
        );
    }
}

pub fn set_material_override_with_history(
    world: &mut World,
    entity: Entity,
    new: MaterialOverride,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<MaterialOverride>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(
        world,
        EditorCommand::SetMaterialOverride { entity, old, new },
    );
}

pub fn set_light_with_history(world: &mut World, entity: Entity, new: LightDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<LightDesc>(entity).cloned();
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetLight { entity, old, new });
}

pub fn set_audio_source_with_history(world: &mut World, entity: Entity, new: AudioSourceDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<AudioSourceDesc>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetAudioSource { entity, old, new });
}

pub fn set_animation_controller_with_history(
    world: &mut World,
    entity: Entity,
    new: AnimationControllerDesc,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<AnimationControllerDesc>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(
        world,
        EditorCommand::SetAnimationController { entity, old, new },
    );
}

pub fn set_audio_listener_with_history(world: &mut World, entity: Entity, new: AudioListenerDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<AudioListenerDesc>(entity).copied();
    if old == Some(new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new);
    }
    push_history(world, EditorCommand::SetAudioListener { entity, old, new });
}

pub fn set_post_process_volume_with_history(
    world: &mut World,
    entity: Entity,
    new: PostProcessVolumeDesc,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<PostProcessVolumeDesc>(entity).cloned();
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(
        world,
        EditorCommand::SetPostProcessVolume { entity, old, new },
    );
}

pub fn navigation_component_state(world: &World, entity: Entity) -> NavigationComponentState {
    NavigationComponentState {
        bounds: world.get::<NavigationBounds>(entity).cloned(),
        obstacle: world.get::<NavigationObstacle>(entity).cloned(),
        area: world.get::<NavigationArea>(entity).cloned(),
        link: world.get::<NavigationLink>(entity).cloned(),
    }
}

pub fn set_navigation_with_history(
    world: &mut World,
    entity: Entity,
    new: NavigationComponentState,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = navigation_component_state(world, entity);
    if old == new {
        return;
    }
    apply_navigation_state(world, entity, &new);
    push_history(world, EditorCommand::SetNavigation { entity, old, new });
}

pub fn set_physics_with_history(world: &mut World, entity: Entity, new: PhysicsBody) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<PhysicsBody>(entity).cloned();
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetPhysics { entity, old, new });
}

pub fn set_rigid_body_with_history(world: &mut World, entity: Entity, new: RigidBodyDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<RigidBodyDesc>(entity).copied();
    if old == Some(new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new);
    }
    push_history(world, EditorCommand::SetRigidBody { entity, old, new });
}

pub fn set_collider_with_history(world: &mut World, entity: Entity, new: ColliderDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<ColliderDesc>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetCollider { entity, old, new });
}

pub fn set_primitive_with_history(world: &mut World, entity: Entity, new: Primitive) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<Primitive>(entity).cloned();
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetPrimitive { entity, old, new });
}

pub fn set_brush_with_history(world: &mut World, entity: Entity, new: BrushDesc) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<BrushDesc>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetBrush { entity, old, new });
}

pub fn set_brush_transform_with_history(
    world: &mut World,
    entity: Entity,
    new_transform: Transform,
    new_brush: BrushDesc,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old_transform = world.get::<Transform>(entity).copied().unwrap_or_default();
    let old_brush = world.get::<BrushDesc>(entity).cloned();
    if old_transform == new_transform && old_brush.as_ref() == Some(&new_brush) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new_transform);
        entity_mut.insert(new_brush.clone());
    }
    push_history(
        world,
        EditorCommand::SetBrushTransform {
            entity,
            old_transform,
            new_transform,
            old_brush,
            new_brush,
        },
    );
}

pub fn apply_brush_csg_with_history(
    world: &mut World,
    primary: Entity,
    new_transform: Transform,
    new_brush: BrushDesc,
    delete_entities: &[Entity],
) {
    if !is_level_object(world, primary) {
        return;
    }
    let old_transform = world.get::<Transform>(primary).copied().unwrap_or_default();
    let old_brush = world.get::<BrushDesc>(primary).cloned();
    let deleted: Vec<(EditorEntitySnapshot, Option<Entity>)> = delete_entities
        .iter()
        .copied()
        .filter_map(|entity| {
            snapshot_entity(world, entity).map(|snapshot| (snapshot, Some(entity)))
        })
        .collect();
    if old_transform == new_transform
        && old_brush.as_ref() == Some(&new_brush)
        && deleted.is_empty()
    {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(primary) {
        entity_mut.insert(new_transform);
        entity_mut.insert(new_brush.clone());
    }
    for (_, entity) in &deleted {
        despawn_entity(world, *entity);
    }
    push_history(
        world,
        EditorCommand::ApplyBrushCsg {
            primary,
            old_transform,
            new_transform,
            old_brush,
            new_brush,
            deleted,
        },
    );
    select_one(world, primary);
}

pub fn set_static_mesh_renderer_with_history(
    world: &mut World,
    entity: Entity,
    new: StaticMeshRenderer,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<StaticMeshRenderer>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(
        world,
        EditorCommand::SetStaticMeshRenderer { entity, old, new },
    );
}

pub fn reparent_with_history(world: &mut World, entity: Entity, new_parent: Option<Entity>) {
    reorder_siblings_with_history(world, &[entity], new_parent, i32::MAX);
}

pub fn reorder_siblings_with_history(
    world: &mut World,
    entities: &[Entity],
    new_parent: Option<Entity>,
    insert_index: i32,
) {
    let entities: Vec<Entity> = entities
        .iter()
        .copied()
        .filter(|entity| is_mutable_level_object(world, *entity))
        .collect();
    if entities.is_empty() {
        return;
    }
    if let Some(violation) = hierarchy_drop_violation(world, &entities, new_parent) {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io.set_status(match violation {
                crate::ui::hierarchy_ops::HierarchyDropViolation::Cycle => {
                    "Hierarchy change blocked: it would create a cycle".to_string()
                }
                crate::ui::hierarchy_ops::HierarchyDropViolation::PrefabBoundary { .. } =>
                    "Hierarchy change blocked: prefab instance contents are read-only; unpack the instance first"
                        .to_string(),
            });
        }
        return;
    }

    let changes = reorder_entities_under_parent(world, &entities, new_parent, insert_index);
    if changes.is_empty() {
        return;
    }
    push_history(world, EditorCommand::ReorderSibling { changes });
}

pub fn set_editor_visibility_with_history(
    world: &mut World,
    entity: Entity,
    new: EditorVisibility,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world
        .get::<EditorVisibility>(entity)
        .copied()
        .unwrap_or_default();
    if old.visible == new.visible {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new);
    }
    push_history(
        world,
        EditorCommand::SetEditorVisibility { entity, old, new },
    );
}

pub fn set_inspector_order_with_history(world: &mut World, entity: Entity, new: InspectorOrder) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    let old = world.get::<InspectorOrder>(entity).cloned();
    if old.as_ref() == Some(&new) {
        return;
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(new.clone());
    }
    push_history(world, EditorCommand::SetInspectorOrder { entity, old, new });
}

pub fn group_selection_with_history(world: &mut World, entities: &[Entity]) {
    let entities: Vec<Entity> = entities
        .iter()
        .copied()
        .filter(|entity| is_mutable_level_object(world, *entity))
        .collect();
    if entities.is_empty() {
        return;
    }

    let snapshot = EditorEntitySnapshot {
        actor_id: None,
        actor_kind: ActorKind::Empty,
        actor_name: None,
        name: Some("Group".to_string()),
        transform: Transform::default(),
        primitive: None,
        brush: None,
        static_mesh_renderer: None,
        skinned_mesh_renderer: None,
        material: None,
        material_override: None,
        rigid_body: None,
        collider: None,
        physics: None,
        light: None,
        animation_controller: None,
        audio_source: None,
        audio_listener: None,
        player_spawn: false,
        model: None,
        prefab: None,
        prefab_instance: None,
        weapon_spawn: None,
        trigger_volume: None,
        post_process_volume: None,
        team_spawn: None,
        objective: None,

        navigation_bounds: None,

        navigation_obstacle: None,

        navigation_area: None,

        navigation_link: None,
        hierarchy_sibling_index: next_sibling_index(world, None),
        editor_visibility: EditorVisibility::default(),
        inspector_order: None,
        component_states: None,
        children: Vec::new(),
    };
    let group = spawn_snapshot(world, &snapshot);
    push_history(
        world,
        EditorCommand::Spawn {
            snapshot,
            entity: Some(group),
        },
    );
    reorder_siblings_with_history(world, &entities, Some(group), i32::MAX);
}

pub fn create_empty_child_with_history(world: &mut World, parent: Entity) {
    if !is_level_object(world, parent) || world.get::<HydratedPrefabMember>(parent).is_some() {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io
                .set_status("Create child blocked: generated prefab source content is read-only");
        }
        return;
    }
    let snapshot = EditorEntitySnapshot {
        actor_id: None,
        actor_kind: ActorKind::Empty,
        actor_name: None,
        name: Some("Empty".to_string()),
        transform: Transform::default(),
        primitive: None,
        brush: None,
        static_mesh_renderer: None,
        skinned_mesh_renderer: None,
        material: None,
        material_override: None,
        rigid_body: None,
        collider: None,
        physics: None,
        light: None,
        animation_controller: None,
        audio_source: None,
        audio_listener: None,
        player_spawn: false,
        model: None,
        prefab: None,
        prefab_instance: None,
        weapon_spawn: None,
        trigger_volume: None,
        post_process_volume: None,
        team_spawn: None,
        objective: None,

        navigation_bounds: None,

        navigation_obstacle: None,

        navigation_area: None,

        navigation_link: None,
        hierarchy_sibling_index: next_sibling_index(world, Some(parent)),
        editor_visibility: EditorVisibility::default(),
        inspector_order: None,
        component_states: None,
        children: Vec::new(),
    };
    let entity = spawn_snapshot_with_parent(world, &snapshot, Some(parent));
    push_history(
        world,
        EditorCommand::Spawn {
            snapshot,
            entity: Some(entity),
        },
    );
    select_one(world, entity);
}

pub fn unpack_prefab_instance(world: &mut World, entity: Entity) {
    let Some(instance) = world.get::<PrefabInstance>(entity).cloned() else {
        return;
    };
    let prefab_ref = world
        .get::<PrefabRef>(entity)
        .cloned()
        .unwrap_or_else(|| PrefabRef::new(instance.source_path.clone()));
    if world.get::<HydratedPrefabReady>(entity).is_none() {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io.set_status("Unpack blocked: prefab source is still loading or unavailable");
        }
        return;
    }
    let linked_actor_kind = world
        .get::<ActorKind>(entity)
        .copied()
        .unwrap_or(ActorKind::PrefabAnchor);
    if let Err(error) =
        crate::assets::prefab_overrides::fold_nested_overrides_for_unpack(world, entity)
    {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io.set_status(format!("Unpack blocked: {error}"));
        }
        return;
    }
    let unpacked_roots = perform_prefab_unpack(world, entity);
    let unpacked_parented_to_anchor = unpacked_roots
        .iter()
        .map(|root| world.get::<ChildOf>(*root).map(ChildOf::parent) == Some(entity))
        .collect();
    let unpacked_snapshots = unpacked_roots
        .iter()
        .filter_map(|root| snapshot_entity(world, *root))
        .collect();
    push_history(
        world,
        EditorCommand::UnpackPrefab {
            entity,
            instance,
            prefab_ref,
            linked_actor_kind,
            fully_local: false,
            linked_local_snapshots: Vec::new(),
            linked_local_roots: Vec::new(),
            unpacked_snapshots,
            unpacked_parented_to_anchor,
            unpacked_roots,
        },
    );
}

pub fn convert_prefab_completely_to_local(world: &mut World, entity: Entity) {
    let Some(instance) = world.get::<PrefabInstance>(entity).cloned() else {
        return;
    };
    let prefab_ref = world
        .get::<PrefabRef>(entity)
        .cloned()
        .unwrap_or_else(|| PrefabRef::new(instance.source_path.clone()));
    let linked_local: Vec<(Entity, EditorEntitySnapshot)> = world
        .get::<Children>(entity)
        .map(|children| {
            children
                .iter()
                .filter(|child| world.get::<HydratedPrefabMember>(*child).is_none())
                .filter_map(|child| snapshot_entity(world, child).map(|snapshot| (child, snapshot)))
                .collect()
        })
        .unwrap_or_default();
    let linked_local_roots: Vec<Entity> = linked_local.iter().map(|(root, _)| *root).collect();
    let linked_local_snapshots = linked_local
        .into_iter()
        .map(|(_, snapshot)| snapshot)
        .collect();

    let mut all_instances: Vec<(usize, Entity)> = world
        .query_filtered::<Entity, With<PrefabInstance>>()
        .iter(world)
        .filter_map(|candidate| {
            prefab_conversion_depth(world, candidate, entity).map(|depth| (depth, candidate))
        })
        .collect();
    all_instances.sort_by_key(|(depth, _)| *depth);
    if all_instances
        .iter()
        .any(|(_, instance_root)| world.get::<HydratedPrefabReady>(*instance_root).is_none())
    {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io
                .set_status("Complete conversion blocked: every nested prefab must finish loading");
        }
        return;
    }

    let linked_actor_kind = world
        .get::<ActorKind>(entity)
        .copied()
        .unwrap_or(ActorKind::PrefabAnchor);
    let mut candidate_roots = linked_local_roots.clone();
    for (_, instance_root) in all_instances {
        candidate_roots.extend(perform_prefab_unpack(world, instance_root));
    }
    let candidate_set: HashSet<_> = candidate_roots.iter().copied().collect();
    candidate_roots.retain(|root| !has_candidate_ancestor(world, *root, &candidate_set));
    let mut unpacked_roots = Vec::new();
    let mut unpacked_snapshots = Vec::new();
    let mut unpacked_parented_to_anchor = Vec::new();
    for root in candidate_roots {
        if let Some(snapshot) = snapshot_entity(world, root) {
            unpacked_parented_to_anchor
                .push(world.get::<ChildOf>(root).map(ChildOf::parent) == Some(entity));
            unpacked_roots.push(root);
            unpacked_snapshots.push(snapshot);
        }
    }
    push_history(
        world,
        EditorCommand::UnpackPrefab {
            entity,
            instance,
            prefab_ref,
            linked_actor_kind,
            fully_local: true,
            linked_local_snapshots,
            linked_local_roots: Vec::new(),
            unpacked_snapshots,
            unpacked_parented_to_anchor,
            unpacked_roots,
        },
    );
}

fn prefab_conversion_depth(world: &World, entity: Entity, root: Entity) -> Option<usize> {
    let mut current = entity;
    let mut depth = 0;
    let mut seen = HashSet::new();
    while seen.insert(current) {
        if current == root {
            return Some(depth);
        }
        current = world
            .get::<HydratedPrefabMember>(current)
            .map(|owner| owner.instance_root)
            .or_else(|| world.get::<ChildOf>(current).map(ChildOf::parent))?;
        depth += 1;
    }
    None
}

fn has_candidate_ancestor(world: &World, entity: Entity, candidates: &HashSet<Entity>) -> bool {
    let mut current = world.get::<ChildOf>(entity).map(ChildOf::parent);
    let mut seen = HashSet::new();
    while let Some(parent) = current {
        if candidates.contains(&parent) {
            return true;
        }
        if !seen.insert(parent) {
            break;
        }
        current = world.get::<ChildOf>(parent).map(ChildOf::parent);
    }
    false
}

fn perform_prefab_unpack(world: &mut World, entity: Entity) -> Vec<Entity> {
    let members: Vec<Entity> = world
        .query::<(Entity, &HydratedPrefabMember)>()
        .iter(world)
        .filter_map(|(member, owner)| (owner.instance_root == entity).then_some(member))
        .collect();
    let member_set: HashSet<Entity> = members.iter().copied().collect();
    let roots = members
        .iter()
        .copied()
        .filter(|member| {
            world
                .get::<ChildOf>(*member)
                .map(ChildOf::parent)
                .is_none_or(|parent| !member_set.contains(&parent))
        })
        .collect();
    for member in members {
        if let Ok(mut member_mut) = world.get_entity_mut(member) {
            member_mut.remove::<HydratedPrefabMember>();
            if member_mut.contains::<LevelObject>() {
                member_mut.insert(new_actor_id());
            }
        }
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut
            .insert(ActorKind::Empty)
            .remove::<PrefabInstance>()
            .remove::<PrefabRef>()
            .remove::<HydratedPrefabReady>()
            .remove::<PrefabHydrationBlocked>()
            .remove::<DynamicWorldRoot>()
            .remove::<WorldInstance>();
    }
    roots
}

fn despawn_hydrated_prefab_members(world: &mut World, entity: Entity) {
    let members: Vec<Entity> = world
        .query::<(Entity, &HydratedPrefabMember)>()
        .iter(world)
        .filter_map(|(member, owner)| (owner.instance_root == entity).then_some(member))
        .collect();
    let member_set: HashSet<Entity> = members.iter().copied().collect();
    let roots: Vec<Entity> = members
        .into_iter()
        .filter(|member| {
            world
                .get::<ChildOf>(*member)
                .map(ChildOf::parent)
                .is_none_or(|parent| !member_set.contains(&parent))
        })
        .collect();
    for root in roots {
        despawn_entity(world, Some(root));
    }
}

pub fn capture_prefab_edit_state(world: &World, entity: Entity) -> PrefabEditState {
    PrefabEditState {
        instance: world.get::<PrefabInstance>(entity).cloned(),
        prefab_ref: world.get::<PrefabRef>(entity).cloned(),
        blocked_reason: world
            .get::<PrefabHydrationBlocked>(entity)
            .map(|blocked| blocked.reason.clone()),
        transform: world.get::<Transform>(entity).copied(),
        material: world.get::<MaterialDesc>(entity).cloned(),
    }
}

pub fn record_prefab_edit_state(world: &mut World, entity: Entity, old: PrefabEditState) {
    let new = capture_prefab_edit_state(world, entity);
    if old != new {
        push_history(world, EditorCommand::SetPrefabState { entity, old, new });
    }
}

pub fn record_prefab_source_apply(
    world: &mut World,
    entity: Entity,
    source_path: std::path::PathBuf,
    before_source: String,
    after_source: String,
    old: PrefabEditState,
) {
    let new = capture_prefab_edit_state(world, entity);
    push_history(
        world,
        EditorCommand::ApplyPrefabToSource {
            entity,
            source_path,
            before_source,
            after_source,
            old,
            new,
        },
    );
}

pub fn set_transform_with_history(
    world: &mut World,
    entity: Entity,
    old: Transform,
    new: Transform,
) {
    if !is_mutable_level_object(world, entity) {
        return;
    }
    if transform_nearly_eq(&old, &new) {
        return;
    }
    if let Some(mut transform) = world.get_mut::<Transform>(entity) {
        *transform = new;
    }
    push_history(world, EditorCommand::SetTransform { entity, old, new });
}

pub fn set_transform_group_with_history(
    world: &mut World,
    changes: impl IntoIterator<Item = (Entity, Transform, Transform)>,
) {
    let mut entities = Vec::new();
    let mut olds = Vec::new();
    let mut news = Vec::new();
    for (entity, old, new) in changes {
        if !is_mutable_level_object(world, entity) {
            continue;
        }
        if transform_nearly_eq(&old, &new) {
            continue;
        }
        if let Some(mut transform) = world.get_mut::<Transform>(entity) {
            *transform = new;
            entities.push(entity);
            olds.push(old);
            news.push(new);
        }
    }
    if !entities.is_empty() {
        push_history(
            world,
            EditorCommand::SetTransformGroup {
                entities,
                olds,
                news,
            },
        );
    }
}

pub fn set_actor_kind_with_history(
    world: &mut World,
    entity: Entity,
    old: ActorKind,
    new: ActorKind,
) {
    if !is_mutable_level_object(world, entity) || old == new {
        return;
    }
    push_history(world, EditorCommand::SetActorKind { entity, old, new });
}

pub fn apply_actor_kind(world: &mut World, entity: Entity, kind: ActorKind) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(kind);
    }
}

pub const ACTOR_KIND_COMPONENT_ID: &str = "core.actor_kind";
pub const ACTOR_KIND_TYPE_PATH: &str = "shared::components::ActorKind";

/// Captures any reflected component without adding a typed history branch.
pub fn capture_reflected_component(
    world: &World,
    entity: Entity,
    component_id: &str,
    type_path: &str,
) -> Result<Option<ReflectedComponentValue>, String> {
    let entity_ref = world
        .get_entity(entity)
        .map_err(|_| format!("entity {entity:?} no longer exists"))?;
    let app_registry = world
        .get_resource::<AppTypeRegistry>()
        .ok_or_else(|| "AppTypeRegistry is unavailable".to_string())?;
    let type_registry = app_registry.read();
    let registration = type_registry
        .get_with_type_path(type_path)
        .ok_or_else(|| format!("component type `{type_path}` is not registered"))?;
    let reflect_component = registration
        .data::<ReflectComponent>()
        .ok_or_else(|| format!("component `{type_path}` has no ReflectComponent adapter"))?;
    let Some(value) = reflect_component.reflect(entity_ref) else {
        return Ok(None);
    };
    let ron = ron::ser::to_string(&TypedReflectSerializer::new(value, &type_registry))
        .map_err(|error| format!("could not serialize `{type_path}`: {error}"))?;
    Ok(Some(ReflectedComponentValue {
        component_id: component_id.to_string(),
        type_path: type_path.to_string(),
        ron,
    }))
}

/// Applies or removes one reflected component value.
pub fn apply_reflected_component(
    world: &mut World,
    entity: Entity,
    type_path: &str,
    value: Option<&ReflectedComponentValue>,
) -> Result<(), String> {
    if world.get_entity(entity).is_err() {
        return Err(format!("entity {entity:?} no longer exists"));
    }
    world.resource_scope(|world, app_registry: Mut<AppTypeRegistry>| {
        let type_registry = app_registry.read();
        let registration = type_registry
            .get_with_type_path(type_path)
            .ok_or_else(|| format!("component type `{type_path}` is not registered"))?;
        let reflect_component = registration
            .data::<ReflectComponent>()
            .cloned()
            .ok_or_else(|| format!("component `{type_path}` has no ReflectComponent adapter"))?;
        match value {
            Some(value) => {
                if value.type_path != type_path {
                    return Err(format!(
                        "clipboard type `{}` does not match `{type_path}`",
                        value.type_path
                    ));
                }
                let mut deserializer = ron::de::Deserializer::from_str(&value.ron)
                    .map_err(|error| error.to_string())?;
                let reflected = TypedReflectDeserializer::new(registration, &type_registry)
                    .deserialize(&mut deserializer)
                    .map_err(|error| error.to_string())?;
                let exists = reflect_component.reflect(world.entity(entity)).is_some();
                if exists {
                    reflect_component.apply(world.entity_mut(entity), reflected.as_ref());
                } else {
                    reflect_component.insert(
                        &mut world.entity_mut(entity),
                        reflected.as_ref(),
                        &type_registry,
                    );
                }
            }
            None => reflect_component.remove(&mut world.entity_mut(entity)),
        }
        Ok(())
    })
}

/// Inserts the reflected `Default` for a registered authoring component.
pub fn apply_reflected_default(
    world: &mut World,
    entity: Entity,
    type_path: &str,
) -> Result<(), String> {
    world.resource_scope(|world, app_registry: Mut<AppTypeRegistry>| {
        let type_registry = app_registry.read();
        let registration = type_registry
            .get_with_type_path(type_path)
            .ok_or_else(|| format!("component type `{type_path}` is not registered"))?;
        let reflect_component = registration
            .data::<ReflectComponent>()
            .cloned()
            .ok_or_else(|| format!("component `{type_path}` has no ReflectComponent adapter"))?;
        let default = registration
            .data::<ReflectDefault>()
            .ok_or_else(|| format!("component `{type_path}` has no reflected Default"))?
            .default();
        let exists = reflect_component.reflect(world.entity(entity)).is_some();
        if exists {
            reflect_component.apply(world.entity_mut(entity), default.as_ref());
        } else {
            reflect_component.insert(
                &mut world.entity_mut(entity),
                default.as_ref(),
                &type_registry,
            );
        }
        Ok(())
    })
}

pub fn sync_actor_kind_hint(world: &mut World, entity: Entity) {
    let kind = 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(kind);
    }
}

/// Executes one atomic reflected component edit and records all changed values,
/// including the derived ActorKind presentation hint, as a single history item.
pub fn reflected_component_transaction(
    world: &mut World,
    entity: Entity,
    label: &'static str,
    component_id: &str,
    type_path: &str,
    edit: impl FnOnce(&mut World, Entity) -> Result<(), String>,
) -> Result<(), String> {
    if !is_mutable_level_object(world, entity) {
        return Err("actor is not editable".to_string());
    }
    let tracked = [
        (component_id, type_path),
        (ACTOR_KIND_COMPONENT_ID, ACTOR_KIND_TYPE_PATH),
    ];
    let before = tracked
        .iter()
        .map(|(id, path)| capture_reflected_component(world, entity, id, path))
        .collect::<Result<Vec<_>, _>>()?;
    if let Err(error) = edit(world, entity) {
        for ((_, path), value) in tracked.iter().zip(before.iter()) {
            let _ = apply_reflected_component(world, entity, path, value.as_ref());
        }
        return Err(error);
    }
    sync_actor_kind_hint(world, entity);
    let after = tracked
        .iter()
        .map(|(id, path)| capture_reflected_component(world, entity, id, path))
        .collect::<Result<Vec<_>, _>>()?;
    let deltas = tracked
        .iter()
        .zip(before)
        .zip(after)
        .filter_map(|(((id, path), before), after)| {
            (before != after).then(|| ComponentDelta {
                component_id: (*id).to_string(),
                type_path: (*path).to_string(),
                before,
                after,
            })
        })
        .collect::<Vec<_>>();
    if !deltas.is_empty() {
        push_history(
            world,
            EditorCommand::ComponentTransaction(ComponentTransaction {
                entity,
                label,
                deltas,
            }),
        );
    }
    Ok(())
}

fn apply_component_transaction(
    world: &mut World,
    transaction: &ComponentTransaction,
    use_after: bool,
) {
    for delta in &transaction.deltas {
        let value = if use_after {
            delta.after.as_ref()
        } else {
            delta.before.as_ref()
        };
        if let Err(error) =
            apply_reflected_component(world, transaction.entity, &delta.type_path, value)
        {
            world.resource_mut::<EditorHistory>().status = format!(
                "{} partially failed for {}: {error}",
                transaction.label, delta.component_id
            );
            break;
        }
    }
}

pub fn set_scene_composition_with_history(world: &mut World, new: SceneComposition) {
    let old = world.resource::<SceneComposition>().clone();
    if old == new {
        return;
    }
    world.insert_resource(new.clone());
    push_history(world, EditorCommand::SetSceneComposition { old, new });
    world.resource_mut::<SceneIo>().request = Some(SceneIoRequest::ReloadComposition);
}

pub fn apply_command_undo(world: &mut World) {
    let candidate = world.resource::<EditorHistory>().undo_stack.last().cloned();
    if let Some(candidate) = candidate {
        if let Err(error) = prepare_prefab_source_history(world, &candidate, true) {
            world.resource_mut::<EditorHistory>().status = format!("Undo blocked: {error}");
            world
                .resource_mut::<SceneIo>()
                .set_status(format!("Undo blocked: {error}"));
            return;
        }
    }
    let Some(mut command) = world.resource_mut::<EditorHistory>().pop_undo() else {
        return;
    };
    let label = command.label();
    undo_command(world, &mut command);
    world.resource_mut::<EditorHistory>().push_redo(command);
    world.resource_mut::<EditorHistory>().set_redo_status(label);
    mark_dirty(world);
}

pub fn apply_command_redo(world: &mut World) {
    let candidate = world.resource::<EditorHistory>().redo_stack.last().cloned();
    if let Some(candidate) = candidate {
        if let Err(error) = prepare_prefab_source_history(world, &candidate, false) {
            world.resource_mut::<EditorHistory>().status = format!("Redo blocked: {error}");
            world
                .resource_mut::<SceneIo>()
                .set_status(format!("Redo blocked: {error}"));
            return;
        }
    }
    let Some(mut command) = world.resource_mut::<EditorHistory>().pop_redo() else {
        return;
    };
    let label = command.label();
    redo_command(world, &mut command);
    world.resource_mut::<EditorHistory>().push_undo(command);
    world.resource_mut::<EditorHistory>().set_undo_status(label);
    mark_dirty(world);
}

fn prepare_prefab_source_history(
    world: &mut World,
    command: &EditorCommand,
    undo: bool,
) -> Result<(), String> {
    let EditorCommand::ApplyPrefabToSource {
        entity,
        source_path,
        before_source,
        after_source,
        ..
    } = command
    else {
        return Ok(());
    };
    let (expected, replacement) = if undo {
        (after_source, before_source)
    } else {
        (before_source, after_source)
    };
    let expected =
        crate::collaboration::FileSnapshot::from_loaded_bytes(source_path, expected.as_bytes());
    crate::collaboration::publish_authored_file(
        world,
        source_path,
        replacement.as_bytes(),
        &expected,
        crate::collaboration::FileWriteIntent::PrefabSourceHistory {
            instance_root: *entity,
        },
    )
    .map(|_| ())
}

fn undo_command(world: &mut World, command: &mut EditorCommand) {
    match command {
        EditorCommand::Spawn { entity, .. } => {
            despawn_entity(world, entity.take());
            clear_selection(world);
        }
        EditorCommand::SpawnMany { entities, .. } => {
            for entity in std::mem::take(entities) {
                despawn_entity(world, Some(entity));
            }
            clear_selection(world);
        }
        EditorCommand::Despawn { snapshot, entity } => {
            let spawned = spawn_snapshot(world, snapshot);
            *entity = Some(spawned);
            select_one(world, spawned);
        }
        EditorCommand::Duplicate { entities, .. } => {
            for entity in std::mem::take(entities) {
                despawn_entity(world, Some(entity));
            }
            clear_selection(world);
        }
        EditorCommand::Rename { entity, old, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(Name::new(old.clone()));
            }
        }
        EditorCommand::SetTransform { entity, old, .. } => {
            if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
                *transform = *old;
            }
        }
        EditorCommand::SetMaterial { entity, old, .. } => {
            apply_material(world, *entity, old);
        }
        EditorCommand::SetMaterialGroup { entities, olds, .. } => {
            for (entity, old) in entities.iter().zip(olds.iter()) {
                apply_material(world, *entity, old);
            }
        }
        EditorCommand::SetMaterialOverride { entity, old, .. } => {
            apply_material_override(world, *entity, old);
        }
        EditorCommand::SetLight { entity, old, .. } => {
            apply_light(world, *entity, old);
        }
        EditorCommand::SetAnimationController { entity, old, .. } => {
            apply_animation_controller(world, *entity, old);
        }
        EditorCommand::SetAudioSource { entity, old, .. } => {
            apply_audio_source(world, *entity, old);
        }
        EditorCommand::SetAudioListener { entity, old, .. } => {
            apply_audio_listener(world, *entity, old);
        }
        EditorCommand::SetPostProcessVolume { entity, old, .. } => {
            apply_post_process_volume(world, *entity, old);
        }
        EditorCommand::SetNavigation { entity, old, .. } => {
            apply_navigation_state(world, *entity, old);
        }
        EditorCommand::SetPhysics { entity, old, .. } => {
            apply_physics(world, *entity, old);
        }
        EditorCommand::SetRigidBody { entity, old, .. } => {
            apply_rigid_body(world, *entity, old);
        }
        EditorCommand::SetCollider { entity, old, .. } => {
            apply_collider(world, *entity, old);
        }
        EditorCommand::SetPrimitive { entity, old, .. } => {
            apply_primitive(world, *entity, old);
        }
        EditorCommand::SetBrush { entity, old, .. } => {
            apply_brush(world, *entity, old);
        }
        EditorCommand::SetBrushTransform {
            entity,
            old_transform,
            old_brush,
            ..
        } => {
            if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
                *transform = *old_transform;
            }
            apply_brush(world, *entity, old_brush);
        }
        EditorCommand::ApplyBrushCsg {
            primary,
            old_transform,
            old_brush,
            deleted,
            ..
        } => {
            if let Some(mut transform) = world.get_mut::<Transform>(*primary) {
                *transform = *old_transform;
            }
            apply_brush(world, *primary, old_brush);
            let mut restored = vec![*primary];
            for (snapshot, entity) in deleted.iter_mut() {
                let spawned = spawn_snapshot(world, snapshot);
                *entity = Some(spawned);
                restored.push(spawned);
            }
            select_many(world, &restored);
        }
        EditorCommand::SetStaticMeshRenderer { entity, old, .. } => {
            apply_static_mesh_renderer(world, *entity, old);
        }
        EditorCommand::SetTransformGroup { entities, olds, .. } => {
            for (entity, old) in entities.iter().zip(olds.iter()) {
                if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
                    *transform = *old;
                }
            }
        }
        EditorCommand::Reparent {
            entity, old_parent, ..
        } => {
            apply_parent(world, *entity, *old_parent);
        }
        EditorCommand::SetActorKind { entity, old, .. } => {
            apply_actor_kind(world, *entity, *old);
        }
        EditorCommand::ReorderSibling { changes } => {
            for change in changes.iter().rev() {
                apply_sibling_change(world, change);
            }
        }
        EditorCommand::SetEditorVisibility { entity, old, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(*old);
            }
        }
        EditorCommand::SetInspectorOrder { entity, old, .. } => {
            apply_inspector_order(world, *entity, old);
        }
        EditorCommand::SetSceneComposition { old, .. } => {
            world.insert_resource(old.clone());
            world.resource_mut::<SceneIo>().request = Some(SceneIoRequest::ReloadComposition);
        }
        EditorCommand::SetPrefabState { entity, old, .. } => {
            apply_prefab_edit_state(world, *entity, old);
        }
        EditorCommand::ApplyPrefabToSource { entity, old, .. } => {
            apply_prefab_edit_state(world, *entity, old);
        }
        EditorCommand::UnpackPrefab {
            entity,
            instance,
            prefab_ref,
            linked_actor_kind,
            linked_local_snapshots,
            linked_local_roots,
            unpacked_roots,
            ..
        } => {
            for root in std::mem::take(unpacked_roots) {
                despawn_entity(world, Some(root));
            }
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert((*linked_actor_kind, instance.clone(), prefab_ref.clone()));
            }
            *linked_local_roots = linked_local_snapshots
                .iter()
                .map(|snapshot| spawn_snapshot_with_parent(world, snapshot, Some(*entity)))
                .collect();
        }
        EditorCommand::AddComponent { entity, snapshot } => {
            remove_authored_components(world, *entity, snapshot);
        }
        EditorCommand::RemoveComponent { entity, snapshot } => {
            apply_authored_components(world, *entity, snapshot);
        }
        EditorCommand::ComponentTransaction(transaction) => {
            apply_component_transaction(world, transaction, false);
        }
    }
}

fn redo_command(world: &mut World, command: &mut EditorCommand) {
    match command {
        EditorCommand::Spawn { snapshot, entity } => {
            let spawned = spawn_snapshot(world, snapshot);
            *entity = Some(spawned);
            select_one(world, spawned);
        }
        EditorCommand::SpawnMany {
            snapshots,
            entities,
        } => {
            let spawned: Vec<Entity> = snapshots
                .iter()
                .map(|snapshot| spawn_snapshot(world, snapshot))
                .collect();
            *entities = spawned.clone();
            select_many(world, &spawned);
        }
        EditorCommand::Despawn { entity, .. } => {
            despawn_entity(world, entity.take());
            clear_selection(world);
        }
        EditorCommand::Duplicate {
            snapshots,
            entities,
        } => {
            let spawned: Vec<Entity> = snapshots
                .iter()
                .map(|snapshot| spawn_snapshot(world, snapshot))
                .collect();
            *entities = spawned.clone();
            select_many(world, &spawned);
        }
        EditorCommand::Rename { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(Name::new(new.clone()));
            }
        }
        EditorCommand::SetTransform { entity, new, .. } => {
            if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
                *transform = *new;
            }
        }
        EditorCommand::SetMaterial { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetMaterialGroup { entities, news, .. } => {
            for (entity, new) in entities.iter().zip(news.iter()) {
                if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                    entity_mut.insert(new.clone());
                }
            }
        }
        EditorCommand::SetMaterialOverride { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetLight { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetAnimationController { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetAudioSource { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetAudioListener { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(*new);
            }
        }
        EditorCommand::SetPostProcessVolume { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetNavigation { entity, new, .. } => {
            apply_navigation_state(world, *entity, new);
        }
        EditorCommand::SetPhysics { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetRigidBody { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(*new);
            }
        }
        EditorCommand::SetCollider { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetPrimitive { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetBrush { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetBrushTransform {
            entity,
            new_transform,
            new_brush,
            ..
        } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(*new_transform);
                entity_mut.insert(new_brush.clone());
            }
        }
        EditorCommand::ApplyBrushCsg {
            primary,
            new_transform,
            new_brush,
            deleted,
            ..
        } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*primary) {
                entity_mut.insert(*new_transform);
                entity_mut.insert(new_brush.clone());
            }
            for (_, entity) in deleted.iter_mut() {
                despawn_entity(world, entity.take());
            }
            select_one(world, *primary);
        }
        EditorCommand::SetStaticMeshRenderer { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetTransformGroup { entities, news, .. } => {
            for (entity, new) in entities.iter().zip(news.iter()) {
                if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
                    *transform = *new;
                }
            }
        }
        EditorCommand::Reparent {
            entity, new_parent, ..
        } => {
            apply_parent(world, *entity, *new_parent);
        }
        EditorCommand::SetActorKind { entity, new, .. } => {
            apply_actor_kind(world, *entity, *new);
        }
        EditorCommand::ReorderSibling { changes } => {
            for change in changes {
                apply_sibling_change_new(world, change);
            }
        }
        EditorCommand::SetEditorVisibility { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(*new);
            }
        }
        EditorCommand::SetInspectorOrder { entity, new, .. } => {
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut.insert(new.clone());
            }
        }
        EditorCommand::SetSceneComposition { new, .. } => {
            world.insert_resource(new.clone());
            world.resource_mut::<SceneIo>().request = Some(SceneIoRequest::ReloadComposition);
        }
        EditorCommand::SetPrefabState { entity, new, .. } => {
            apply_prefab_edit_state(world, *entity, new);
        }
        EditorCommand::ApplyPrefabToSource { entity, new, .. } => {
            apply_prefab_edit_state(world, *entity, new);
        }
        EditorCommand::UnpackPrefab {
            entity,
            linked_local_roots,
            unpacked_snapshots,
            unpacked_parented_to_anchor,
            unpacked_roots,
            ..
        } => {
            for root in std::mem::take(linked_local_roots) {
                despawn_entity(world, Some(root));
            }
            despawn_hydrated_prefab_members(world, *entity);
            if let Ok(mut entity_mut) = world.get_entity_mut(*entity) {
                entity_mut
                    .insert(ActorKind::Empty)
                    .remove::<PrefabInstance>()
                    .remove::<PrefabRef>()
                    .remove::<HydratedPrefabReady>()
                    .remove::<PrefabHydrationBlocked>()
                    .remove::<DynamicWorldRoot>()
                    .remove::<WorldInstance>();
            }
            *unpacked_roots = unpacked_snapshots
                .iter()
                .zip(unpacked_parented_to_anchor.iter())
                .map(|(snapshot, parented)| {
                    spawn_snapshot_with_parent(world, snapshot, parented.then_some(*entity))
                })
                .collect();
        }
        EditorCommand::AddComponent { entity, snapshot } => {
            apply_authored_components(world, *entity, snapshot);
        }
        EditorCommand::RemoveComponent { entity, snapshot } => {
            remove_authored_components(world, *entity, snapshot);
        }
        EditorCommand::ComponentTransaction(transaction) => {
            apply_component_transaction(world, transaction, true);
        }
    }
}

fn apply_material(world: &mut World, entity: Entity, material: &Option<MaterialDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match material {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<MaterialDesc>();
            }
        }
    }
}

fn apply_prefab_edit_state(world: &mut World, entity: Entity, state: &PrefabEditState) {
    if state.blocked_reason.is_some() {
        despawn_hydrated_prefab_members(world, entity);
    }
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match &state.instance {
            Some(instance) => {
                entity_mut.insert(instance.clone());
            }
            None => {
                entity_mut.remove::<PrefabInstance>();
            }
        }
        match &state.prefab_ref {
            Some(prefab_ref) => {
                entity_mut.insert(prefab_ref.clone());
            }
            None => {
                entity_mut.remove::<PrefabRef>();
            }
        }
        match &state.blocked_reason {
            Some(reason) => {
                entity_mut
                    .insert(PrefabHydrationBlocked {
                        reason: reason.clone(),
                    })
                    .remove::<HydratedPrefabReady>()
                    .remove::<DynamicWorldRoot>()
                    .remove::<WorldInstance>();
            }
            None => {
                entity_mut.remove::<PrefabHydrationBlocked>();
            }
        }
        if let Some(transform) = state.transform {
            entity_mut.insert(transform);
        }
        match &state.material {
            Some(material) => {
                entity_mut.insert(material.clone());
            }
            None => {
                entity_mut.remove::<MaterialDesc>();
            }
        }
    }
    crate::assets::prefab_overrides::apply_stored_overrides(world, entity);
}

fn apply_material_override(
    world: &mut World,
    entity: Entity,
    material_override: &Option<MaterialOverride>,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match material_override {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<MaterialOverride>();
            }
        }
    }
}

fn apply_light(world: &mut World, entity: Entity, light: &Option<LightDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match light {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<LightDesc>();
            }
        }
    }
}

fn apply_animation_controller(
    world: &mut World,
    entity: Entity,
    controller: &Option<AnimationControllerDesc>,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match controller {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<AnimationControllerDesc>();
            }
        }
    }
}

fn apply_audio_source(world: &mut World, entity: Entity, source: &Option<AudioSourceDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match source {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<AudioSourceDesc>();
            }
        }
    }
}

fn apply_audio_listener(world: &mut World, entity: Entity, listener: &Option<AudioListenerDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match listener {
            Some(value) => {
                entity_mut.insert(*value);
            }
            None => {
                entity_mut.remove::<AudioListenerDesc>();
            }
        }
    }
}

fn apply_post_process_volume(
    world: &mut World,
    entity: Entity,
    volume: &Option<PostProcessVolumeDesc>,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match volume {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<PostProcessVolumeDesc>();
            }
        }
    }
}

fn apply_navigation_state(world: &mut World, entity: Entity, state: &NavigationComponentState) {
    let has_navigation = state.bounds.is_some()
        || state.obstacle.is_some()
        || state.area.is_some()
        || state.link.is_some();
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut
            .remove::<NavigationBounds>()
            .remove::<NavigationObstacle>()
            .remove::<NavigationArea>()
            .remove::<NavigationLink>();
        if let Some(value) = &state.bounds {
            entity_mut.insert(value.clone());
        }
        if let Some(value) = &state.obstacle {
            entity_mut.insert(value.clone());
        }
        if let Some(value) = &state.area {
            entity_mut.insert(value.clone());
        }
        if let Some(value) = &state.link {
            entity_mut.insert(value.clone());
        }
        if has_navigation {
            entity_mut.insert(ActorKind::Navigation);
        }
    }
    if !has_navigation {
        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);
        }
    }
}

fn apply_physics(world: &mut World, entity: Entity, physics: &Option<PhysicsBody>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match physics {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<PhysicsBody>();
            }
        }
    }
}

fn apply_rigid_body(world: &mut World, entity: Entity, rigid_body: &Option<RigidBodyDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match rigid_body {
            Some(value) => {
                entity_mut.insert(*value);
            }
            None => {
                entity_mut.remove::<RigidBodyDesc>();
            }
        }
    }
}

fn apply_collider(world: &mut World, entity: Entity, collider: &Option<ColliderDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match collider {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<ColliderDesc>();
            }
        }
    }
}

fn apply_primitive(world: &mut World, entity: Entity, primitive: &Option<Primitive>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match primitive {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<Primitive>();
            }
        }
    }
}

fn apply_brush(world: &mut World, entity: Entity, brush: &Option<BrushDesc>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match brush {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<BrushDesc>();
            }
        }
    }
}

fn apply_static_mesh_renderer(
    world: &mut World,
    entity: Entity,
    renderer: &Option<StaticMeshRenderer>,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match renderer {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<StaticMeshRenderer>();
            }
        }
    }
}

fn apply_inspector_order(
    world: &mut World,
    entity: Entity,
    inspector_order: &Option<InspectorOrder>,
) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match inspector_order {
            Some(value) => {
                entity_mut.insert(value.clone());
            }
            None => {
                entity_mut.remove::<InspectorOrder>();
            }
        }
    }
}

fn apply_parent(world: &mut World, entity: Entity, parent: Option<Entity>) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        match parent {
            Some(p) => {
                entity_mut.insert(ChildOf(p));
            }
            None => {
                entity_mut.remove::<ChildOf>();
            }
        }
    }
}

fn apply_authored_components(world: &mut World, entity: Entity, snapshot: &EditorEntitySnapshot) {
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        entity_mut.insert(snapshot.actor_kind);
        if let Some(v) = &snapshot.primitive {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.brush {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.static_mesh_renderer {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.skinned_mesh_renderer {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.material {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.material_override {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.rigid_body {
            entity_mut.insert(*v);
        }
        if let Some(v) = &snapshot.collider {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.physics {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.light {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.animation_controller {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.audio_source {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = snapshot.audio_listener {
            entity_mut.insert(v);
        }
        if snapshot.player_spawn {
            entity_mut.insert(PlayerSpawn);
        }
        if let Some(v) = &snapshot.model {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.prefab {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.prefab_instance {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.weapon_spawn {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.trigger_volume {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.post_process_volume {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.team_spawn {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.objective {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.navigation_bounds {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.navigation_obstacle {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.navigation_area {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.navigation_link {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.inspector_order {
            entity_mut.insert(v.clone());
        }
        if let Some(v) = &snapshot.component_states {
            entity_mut.insert(v.clone());
        }
    }
}

fn remove_authored_components(world: &mut World, entity: Entity, snapshot: &EditorEntitySnapshot) {
    let removed_dedicated_audio_kind = matches!(
        world.get::<ActorKind>(entity).copied(),
        Some(ActorKind::AudioSource) if snapshot.audio_source.is_some()
    ) || matches!(
        world.get::<ActorKind>(entity).copied(),
        Some(ActorKind::AudioListener) if snapshot.audio_listener.is_some()
    );
    if let Ok(mut entity_mut) = world.get_entity_mut(entity) {
        if snapshot.primitive.is_some() {
            entity_mut.remove::<Primitive>();
        }
        if snapshot.brush.is_some() {
            entity_mut.remove::<BrushDesc>();
        }
        if snapshot.static_mesh_renderer.is_some() {
            entity_mut.remove::<StaticMeshRenderer>();
        }
        if snapshot.skinned_mesh_renderer.is_some() {
            entity_mut.remove::<SkinnedMeshRenderer>();
        }
        if snapshot.material.is_some() {
            entity_mut.remove::<MaterialDesc>();
        }
        if snapshot.material_override.is_some() {
            entity_mut.remove::<MaterialOverride>();
        }
        if snapshot.rigid_body.is_some() {
            entity_mut.remove::<RigidBodyDesc>();
        }
        if snapshot.collider.is_some() {
            entity_mut.remove::<ColliderDesc>();
        }
        if snapshot.physics.is_some() {
            entity_mut.remove::<PhysicsBody>();
        }
        if snapshot.light.is_some() {
            entity_mut.remove::<LightDesc>();
        }
        if snapshot.animation_controller.is_some() {
            entity_mut.remove::<AnimationControllerDesc>();
        }
        if snapshot.audio_source.is_some() {
            entity_mut.remove::<AudioSourceDesc>();
        }
        if snapshot.audio_listener.is_some() {
            entity_mut.remove::<AudioListenerDesc>();
        }
        if snapshot.player_spawn {
            entity_mut.remove::<PlayerSpawn>();
        }
        if snapshot.model.is_some() {
            entity_mut.remove::<ModelRef>();
        }
        if snapshot.prefab.is_some() {
            entity_mut.remove::<PrefabRef>();
        }
        if snapshot.prefab_instance.is_some() {
            entity_mut.remove::<PrefabInstance>();
        }
        if snapshot.weapon_spawn.is_some() {
            entity_mut.remove::<WeaponSpawn>();
        }
        if snapshot.trigger_volume.is_some() {
            entity_mut.remove::<TriggerVolume>();
        }
        if snapshot.post_process_volume.is_some() {
            entity_mut.remove::<PostProcessVolumeDesc>();
        }
        if snapshot.team_spawn.is_some() {
            entity_mut.remove::<TeamSpawn>();
        }
        if snapshot.objective.is_some() {
            entity_mut.remove::<ObjectiveMarker>();
        }
        if snapshot.navigation_bounds.is_some() {
            entity_mut.remove::<NavigationBounds>();
        }
        if snapshot.navigation_obstacle.is_some() {
            entity_mut.remove::<NavigationObstacle>();
        }
        if snapshot.navigation_area.is_some() {
            entity_mut.remove::<NavigationArea>();
        }
        if snapshot.navigation_link.is_some() {
            entity_mut.remove::<NavigationLink>();
        }
        if snapshot.inspector_order.is_some() {
            entity_mut.remove::<InspectorOrder>();
        }
        if snapshot.component_states.is_some() {
            entity_mut.remove::<AuthoringComponentStates>();
        }
    }
    if removed_dedicated_audio_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);
        }
    }
}

fn handle_history_hotkeys(world: &mut World) {
    if egui_captures_keyboard_from_world(world) {
        return;
    }

    let (undo, redo, delete, duplicate) = {
        let keys = world.resource::<ButtonInput<KeyCode>>();
        let ctrl = keys.any_pressed([KeyCode::ControlLeft, KeyCode::ControlRight]);
        let shift = keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]);
        (
            ctrl && keys.just_pressed(KeyCode::KeyZ) && !shift,
            ctrl && (keys.just_pressed(KeyCode::KeyY)
                || (shift && keys.just_pressed(KeyCode::KeyZ))),
            keys.just_pressed(KeyCode::Delete) || keys.just_pressed(KeyCode::Backspace),
            ctrl && keys.just_pressed(KeyCode::KeyD),
        )
    };

    if undo {
        apply_command_undo(world);
        return;
    }
    if redo {
        apply_command_redo(world);
        return;
    }

    let selection: Vec<Entity> = world
        .get_resource::<UiState>()
        .map(|ui_state| {
            ui_state
                .selected_entities
                .iter()
                .filter(|entity| is_mutable_level_object(world, *entity))
                .collect()
        })
        .unwrap_or_default();
    if delete && !selection.is_empty() {
        delete_entities_with_history(world, &selection);
    } else if duplicate && !selection.is_empty() {
        duplicate_entities_with_history(world, &selection);
    }
}

fn capture_gizmo_transform_edits(world: &mut World) {
    let selection: Vec<Entity> = world
        .get_resource::<UiState>()
        .map(|ui| {
            ui.selected_entities
                .iter()
                .filter(|entity| is_mutable_level_object(world, *entity))
                .collect()
        })
        .unwrap_or_default();

    let mut query = world.query::<(
        Entity,
        &Transform,
        &transform_gizmo_bevy::prelude::GizmoTarget,
        Option<&crate::viewport::brush_edit::BrushElementGizmo>,
    )>();
    let active = query
        .iter(world)
        .find(|(_, _, target, brush_element_gizmo)| {
            brush_element_gizmo.is_none() && target.is_active()
        })
        .map(|(entity, transform, _, _)| (entity, *transform));

    let start_group =
        active.map(|(entity, transform)| selected_transforms(world, &selection, entity, transform));

    let (finished_edit, live_edit) = {
        let mut tracker = world.resource_mut::<TransformEditTracker>();
        if tracker.active.is_none() {
            if let (Some((entity, transform)), Some(group)) = (active, start_group) {
                tracker.active = Some((entity, transform, group));
                return;
            }
        }
        match tracker.active.take() {
            Some((primary, old, group)) if active.is_none() => (Some((primary, old, group)), None),
            Some(state) if active.is_some() => {
                let live = Some(state.clone());
                tracker.active = Some(state);
                (None, live)
            }
            _ => (None, None),
        }
    };

    if let Some((primary, old_primary, group)) = live_edit {
        let Some(new_primary) = world.get::<Transform>(primary).copied() else {
            return;
        };
        if group.len() > 1 {
            let changes = transform_group_changes(old_primary, new_primary, group);
            apply_transform_group(world, &changes);
        }
    }

    if let Some((primary, old_primary, group)) = finished_edit {
        let Some(new_primary) = world.get::<Transform>(primary).copied() else {
            return;
        };
        if group.len() <= 1 {
            set_transform_with_history(world, primary, old_primary, new_primary);
            return;
        }
        let changes = transform_group_changes(old_primary, new_primary, group);
        set_transform_group_with_history(world, changes);
    }
}

fn transform_group_changes(
    old_primary: Transform,
    new_primary: Transform,
    group: Vec<(Entity, Transform)>,
) -> Vec<(Entity, Transform, Transform)> {
    let translation_delta = new_primary.translation - old_primary.translation;
    let rotation_delta = new_primary.rotation * old_primary.rotation.inverse();
    let scale_ratio = new_primary.scale / old_primary.scale;
    group
        .into_iter()
        .map(|(entity, old)| {
            let new = Transform {
                translation: old.translation + translation_delta,
                rotation: rotation_delta * old.rotation,
                scale: old.scale * scale_ratio,
            };
            (entity, old, new)
        })
        .collect()
}

fn apply_transform_group(world: &mut World, changes: &[(Entity, Transform, Transform)]) {
    for (entity, _, new) in changes {
        if let Some(mut transform) = world.get_mut::<Transform>(*entity) {
            *transform = *new;
        }
    }
}

fn selected_transforms(
    world: &World,
    selection: &[Entity],
    primary: Entity,
    primary_transform: Transform,
) -> Vec<(Entity, Transform)> {
    if selection.len() > 1 && selection.contains(&primary) {
        selection
            .iter()
            .filter_map(|entity| {
                world
                    .get::<Transform>(*entity)
                    .copied()
                    .map(|transform| (*entity, transform))
            })
            .collect()
    } else {
        vec![(primary, primary_transform)]
    }
}

fn push_history(world: &mut World, command: EditorCommand) {
    world.resource_mut::<EditorHistory>().push(command);
    mark_dirty(world);
}

pub fn push_command(world: &mut World, command: EditorCommand) {
    push_history(world, command);
}

fn mark_dirty(world: &mut World) {
    if let Some(mut io) = world.get_resource_mut::<SceneIo>() {
        io.mark_dirty();
    }
}

fn despawn_entity(world: &mut World, entity: Option<Entity>) {
    if let Some(entity) = entity {
        if let Ok(entity_mut) = world.get_entity_mut(entity) {
            entity_mut.despawn();
        }
    }
}

fn select_one(world: &mut World, entity: Entity) {
    if let Some(mut ui_state) = world.get_resource_mut::<UiState>() {
        ui_state.selected_entities.clear();
        ui_state.selected_entities.select_replace(entity);
    }
    world.resource_mut::<SelectedEntity>().0 = Some(entity);
}

fn select_many(world: &mut World, entities: &[Entity]) {
    if let Some(mut ui_state) = world.get_resource_mut::<UiState>() {
        ui_state.selected_entities.clear();
        for (index, entity) in entities.iter().enumerate() {
            if index == 0 {
                ui_state.selected_entities.select_replace(*entity);
            } else {
                ui_state.selected_entities.select_maybe_add(*entity, true);
            }
        }
    }
    world.resource_mut::<SelectedEntity>().0 = entities.first().copied();
}

pub fn clear_selection(world: &mut World) {
    if let Some(mut ui_state) = world.get_resource_mut::<UiState>() {
        ui_state.selected_entities.clear();
    }
    world.resource_mut::<SelectedEntity>().0 = None;
}

pub fn is_level_object(world: &World, entity: Entity) -> bool {
    world
        .get_entity(entity)
        .is_ok_and(|entity_ref| entity_ref.contains::<LevelObject>())
}

fn transform_nearly_eq(a: &Transform, b: &Transform) -> bool {
    a.translation.abs_diff_eq(b.translation, 0.0001)
        && a.rotation.abs_diff_eq(b.rotation, 0.0001)
        && a.scale.abs_diff_eq(b.scale, 0.0001)
}

pub(crate) fn material_eq(a: &MaterialDesc, b: &MaterialDesc) -> bool {
    a.shader == b.shader
        && a.base_color.r == b.base_color.r
        && a.base_color.g == b.base_color.g
        && a.base_color.b == b.base_color.b
        && a.base_color.a == b.base_color.a
        && a.metallic == b.metallic
        && a.roughness == b.roughness
        && a.emissive_color.r == b.emissive_color.r
        && a.emissive_color.g == b.emissive_color.g
        && a.emissive_color.b == b.emissive_color.b
        && a.emissive_color.a == b.emissive_color.a
        && a.emissive_intensity == b.emissive_intensity
        && a.base_color_texture == b.base_color_texture
        && a.emissive_texture == b.emissive_texture
        && a.normal_map_texture == b.normal_map_texture
        && a.metallic_roughness_texture == b.metallic_roughness_texture
        && a.material_asset_path == b.material_asset_path
        && a.parameters == b.parameters
        && a.textures == b.textures
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::operators::test_harness::{assert_undo_redo_round_trip, OperatorInvariantHarness};
    use crate::operators::{ActiveOperator, OperatorPhase, OperatorStatus};

    #[derive(Component, Reflect, Default, Debug, Clone, PartialEq)]
    #[reflect(Component, Default)]
    struct StaticExtensionFixture {
        value: u32,
    }

    #[test]
    fn reflected_extension_component_transaction_round_trips_atomically() {
        let mut app = App::new();
        app.register_type::<ActorKind>()
            .register_type::<StaticExtensionFixture>();
        let world = app.world_mut();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let entity = world
            .spawn((LevelObject, ActorKind::Light, Transform::default()))
            .id();
        let type_path = std::any::type_name::<StaticExtensionFixture>();

        reflected_component_transaction(
            world,
            entity,
            "Add Fixture",
            "game.static_extension_fixture",
            type_path,
            |world, entity| {
                world
                    .entity_mut(entity)
                    .insert(StaticExtensionFixture { value: 42 });
                Ok(())
            },
        )
        .unwrap();

        assert_eq!(
            world.get::<StaticExtensionFixture>(entity).unwrap().value,
            42
        );
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Empty));
        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 1);

        apply_command_undo(world);
        assert!(world.get::<StaticExtensionFixture>(entity).is_none());
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Light));

        apply_command_redo(world);
        assert_eq!(
            world.get::<StaticExtensionFixture>(entity).unwrap().value,
            42
        );
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Empty));
    }

    #[test]
    fn snapshots_preserve_component_order_and_independent_active_state() {
        let mut world = World::new();
        let order = InspectorOrder {
            component_ids: vec![shared::AUTHORING_COMPONENT_LIGHT.to_string()],
            ..Default::default()
        };
        let mut states = AuthoringComponentStates::default();
        states.set_component_active(shared::COMPONENT_LIGHT_DESC, false);
        let entity = world
            .spawn((
                LevelObject,
                ActorKind::Light,
                Transform::default(),
                LightDesc::default(),
                order.clone(),
                states.clone(),
            ))
            .id();

        let snapshot = snapshot_entity(&world, entity).unwrap();
        world.entity_mut(entity).despawn();
        let restored = spawn_snapshot(&mut world, &snapshot);

        assert_eq!(world.get::<InspectorOrder>(restored), Some(&order));
        assert_eq!(
            world.get::<AuthoringComponentStates>(restored),
            Some(&states)
        );
    }

    fn animation_controller(state_id: &str, crossfade_seconds: f32) -> AnimationControllerDesc {
        AnimationControllerDesc {
            skeleton: Some(
                shared::EditorAssetRef::new("robot-asset", "animation:skeleton:0:robot", "Robot")
                    .with_source_path("assets/models/robot.glb"),
            ),
            states: vec![shared::AnimationStateDesc {
                id: state_id.to_string(),
                label: state_id.to_ascii_uppercase(),
                clip: shared::EditorAssetRef::new(
                    "robot-asset",
                    format!("animation:clip:0:{state_id}"),
                    state_id,
                )
                .with_source_path("assets/models/robot.glb"),
                looping: true,
                speed: 1.25,
                range: shared::AnimationPlaybackRange {
                    start_seconds: 0.1,
                    end_seconds: Some(1.8),
                },
            }],
            default_state: state_id.to_string(),
            default_crossfade_seconds: crossfade_seconds,
        }
    }

    #[test]
    fn animation_controller_history_round_trips_and_marks_scene_dirty() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let old = animation_controller("idle", 0.15);
        let new = animation_controller("run", 0.3);
        let entity = world
            .spawn((
                LevelObject,
                ActorKind::SkinnedMesh,
                Transform::IDENTITY,
                old.clone(),
            ))
            .id();

        set_animation_controller_with_history(&mut world, entity, new.clone());

        assert_eq!(world.get::<AnimationControllerDesc>(entity), Some(&new));
        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 1);
        assert!(world.resource::<SceneIo>().dirty);

        apply_command_undo(&mut world);
        assert_eq!(world.get::<AnimationControllerDesc>(entity), Some(&old));

        apply_command_redo(&mut world);
        assert_eq!(world.get::<AnimationControllerDesc>(entity), Some(&new));
    }

    #[test]
    fn navigation_component_history_round_trips_and_preserves_kind() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let entity = world
            .spawn((LevelObject, ActorKind::Empty, Transform::IDENTITY))
            .id();
        let bounds = NavigationBounds::default();

        set_navigation_with_history(
            &mut world,
            entity,
            NavigationComponentState {
                bounds: Some(bounds.clone()),
                ..Default::default()
            },
        );
        assert_eq!(world.get::<NavigationBounds>(entity), Some(&bounds));
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Navigation));

        apply_command_undo(&mut world);
        assert!(world.get::<NavigationBounds>(entity).is_none());
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Empty));

        apply_command_redo(&mut world);
        assert_eq!(world.get::<NavigationBounds>(entity), Some(&bounds));
        assert_eq!(world.get::<ActorKind>(entity), Some(&ActorKind::Navigation));
    }

    #[test]
    fn undoing_a_new_animation_controller_removes_the_component() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let controller = animation_controller("idle", 0.2);
        let entity = world
            .spawn((LevelObject, ActorKind::SkinnedMesh, Transform::IDENTITY))
            .id();

        set_animation_controller_with_history(&mut world, entity, controller.clone());
        apply_command_undo(&mut world);
        assert!(world.get::<AnimationControllerDesc>(entity).is_none());

        apply_command_redo(&mut world);
        assert_eq!(
            world.get::<AnimationControllerDesc>(entity),
            Some(&controller)
        );
    }

    #[test]
    fn animation_controller_survives_snapshot_respawn() {
        let mut world = World::new();
        let controller = animation_controller("idle", 0.2);
        let source = world
            .spawn((
                LevelObject,
                ActorKind::SkinnedMesh,
                Name::new("Animated Robot"),
                Transform::from_xyz(1.0, 2.0, 3.0),
                SkinnedMeshRenderer::new("assets/models/robot.glb").with_asset_id("robot-asset"),
                controller.clone(),
            ))
            .id();
        let snapshot = snapshot_entity(&world, source).expect("level object is snapshotable");

        world.entity_mut(source).despawn();
        let respawned = spawn_snapshot(&mut world, &snapshot);

        assert_eq!(
            world.get::<AnimationControllerDesc>(respawned),
            Some(&controller)
        );
        assert_eq!(
            world.get::<Name>(respawned).map(Name::as_str),
            Some("Animated Robot")
        );
        assert_eq!(
            world.get::<SkinnedMeshRenderer>(respawned),
            Some(&SkinnedMeshRenderer::new("assets/models/robot.glb").with_asset_id("robot-asset"))
        );
    }

    #[test]
    fn removing_dedicated_audio_components_repairs_and_restores_actor_kind() {
        let mut world = World::new();
        let source = world
            .spawn((
                LevelObject,
                ActorKind::AudioSource,
                Transform::IDENTITY,
                AudioSourceDesc::default(),
            ))
            .id();
        let source_snapshot = snapshot_entity(&world, source).unwrap();

        remove_authored_components(&mut world, source, &source_snapshot);
        assert!(world.get::<AudioSourceDesc>(source).is_none());
        assert_eq!(world.get::<ActorKind>(source), Some(&ActorKind::Empty));

        apply_authored_components(&mut world, source, &source_snapshot);
        assert!(world.get::<AudioSourceDesc>(source).is_some());
        assert_eq!(
            world.get::<ActorKind>(source),
            Some(&ActorKind::AudioSource)
        );

        let listener = world
            .spawn((
                LevelObject,
                ActorKind::AudioListener,
                Transform::IDENTITY,
                AudioListenerDesc::default(),
            ))
            .id();
        let listener_snapshot = snapshot_entity(&world, listener).unwrap();

        remove_authored_components(&mut world, listener, &listener_snapshot);
        assert!(world.get::<AudioListenerDesc>(listener).is_none());
        assert_eq!(world.get::<ActorKind>(listener), Some(&ActorKind::Empty));

        apply_authored_components(&mut world, listener, &listener_snapshot);
        assert!(world.get::<AudioListenerDesc>(listener).is_some());
        assert_eq!(
            world.get::<ActorKind>(listener),
            Some(&ActorKind::AudioListener)
        );
    }

    #[test]
    fn continuous_multi_transform_finalizes_as_one_undo_group() {
        let mut world = World::new();
        world.init_resource::<ActiveOperator>();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let first = world.spawn((LevelObject, Transform::default())).id();
        let second = world
            .spawn((
                LevelObject,
                Transform::from_translation(Vec3::new(2.0, 0.0, 0.0)),
            ))
            .id();
        let initial = vec![Vec3::ZERO, Vec3::new(2.0, 0.0, 0.0)];
        let committed = vec![Vec3::Z, Vec3::new(2.0, 0.0, 1.0)];
        let harness = OperatorInvariantHarness::capture(&mut world);

        set_transform_group_with_history(
            &mut world,
            [
                (
                    first,
                    Transform::default(),
                    Transform::from_translation(committed[0]),
                ),
                (
                    second,
                    Transform::from_translation(initial[1]),
                    Transform::from_translation(committed[1]),
                ),
            ],
        );
        world.resource_mut::<ActiveOperator>().status = Some(OperatorStatus {
            id: "transform.gizmo".to_string(),
            label: "Transform Gizmo".to_string(),
            phase: OperatorPhase::Committed,
            hint: "Committed multi-selection drag".to_string(),
            warnings: Vec::new(),
        });

        harness.assert_committed(&mut world, 1, 0);
        assert_undo_redo_round_trip(&mut world, initial, committed, |world| {
            [first, second]
                .into_iter()
                .map(|entity| world.get::<Transform>(entity).unwrap().translation)
                .collect::<Vec<_>>()
        });
    }

    #[test]
    fn unpack_prefab_is_one_undoable_history_command() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let instance = PrefabInstance::new("asset", "assets/prefabs/crate.scn.ron");
        let root = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                instance.clone(),
                PrefabRef::new(instance.source_path.clone()),
                HydratedPrefabReady,
            ))
            .id();
        let generated = world
            .spawn((
                LevelObject,
                ActorId::new("generated-child"),
                ChildOf(root),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();

        unpack_prefab_instance(&mut world, root);

        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 1);
        assert!(world.get::<PrefabInstance>(root).is_none());
        assert!(world.get::<PrefabRef>(root).is_none());
        assert_eq!(world.get::<ActorKind>(root), Some(&ActorKind::Empty));
        assert!(world.get::<HydratedPrefabMember>(generated).is_none());
        let unpacked_actor_id = world.get::<ActorId>(generated).unwrap().0.clone();
        assert_ne!(unpacked_actor_id, "generated-child");

        apply_command_undo(&mut world);
        assert_eq!(world.get::<PrefabInstance>(root), Some(&instance));
        assert!(world.get::<PrefabRef>(root).is_some());
        assert_eq!(world.get::<ActorKind>(root), Some(&ActorKind::PrefabAnchor));
        assert!(world.get_entity(generated).is_err());

        world
            .entity_mut(root)
            .insert(PrefabHydrationBlocked {
                reason: "source removed after undo".to_string(),
            })
            .remove::<PrefabRef>();
        apply_command_redo(&mut world);
        assert!(world.get::<PrefabInstance>(root).is_none());
        assert!(world.get::<PrefabRef>(root).is_none());
        assert!(world.get::<PrefabHydrationBlocked>(root).is_none());
        assert_eq!(world.get::<ActorKind>(root), Some(&ActorKind::Empty));
        assert!(!world.resource::<EditorHistory>().can_redo());
        let restored = world
            .query::<(Entity, &ActorId)>()
            .iter(&world)
            .find_map(|(entity, actor_id)| (actor_id.0 == unpacked_actor_id).then_some(entity))
            .expect("redo should restore the committed local actor without source readiness");
        assert_eq!(
            world.get::<ChildOf>(restored).map(ChildOf::parent),
            Some(root)
        );
        assert_eq!(world.get::<ActorId>(restored).unwrap().0, unpacked_actor_id);
    }

    #[test]
    fn unpack_redo_uses_committed_snapshot_instead_of_mutated_source() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let instance = PrefabInstance::new("asset", "assets/prefabs/crate.scn.ron");
        let root = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                instance.clone(),
                PrefabRef::new(instance.source_path.clone()),
                HydratedPrefabReady,
            ))
            .id();
        let generated = world
            .spawn((
                LevelObject,
                ActorKind::StaticMesh,
                ActorId::new("source-child"),
                Name::new("Committed Source Actor"),
                Transform::from_xyz(1.0, 2.0, 3.0),
                ChildOf(root),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();

        unpack_prefab_instance(&mut world, root);
        let committed_actor_id = world.get::<ActorId>(generated).unwrap().0.clone();
        apply_command_undo(&mut world);

        let mutated_source = world
            .spawn((
                LevelObject,
                ActorKind::Light,
                ActorId::new("source-child"),
                Name::new("Mutated Source Actor"),
                Transform::from_xyz(90.0, 80.0, 70.0),
                ChildOf(root),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();
        world.entity_mut(root).insert(HydratedPrefabReady);

        apply_command_redo(&mut world);

        assert!(world.get_entity(mutated_source).is_err());
        let restored = world
            .query::<(Entity, &ActorId)>()
            .iter(&world)
            .find_map(|(entity, actor_id)| (actor_id.0 == committed_actor_id).then_some(entity))
            .expect("redo should recreate the originally committed local actor");
        assert_eq!(
            world.get::<Name>(restored).map(Name::as_str),
            Some("Committed Source Actor")
        );
        assert_eq!(
            world.get::<Transform>(restored).unwrap().translation,
            Vec3::new(1.0, 2.0, 3.0)
        );
        assert_eq!(
            world.get::<ActorKind>(restored),
            Some(&ActorKind::StaticMesh)
        );
        assert!(world.get::<HydratedPrefabMember>(restored).is_none());
    }

    #[test]
    fn unpack_redo_preserves_structurally_unparented_roots() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let root = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                PrefabInstance::new("asset", "assets/prefabs/test.scn.ron"),
                PrefabRef::new("assets/prefabs/test.scn.ron"),
                HydratedPrefabReady,
            ))
            .id();
        let generated = world
            .spawn((
                LevelObject,
                ActorId::new("unparented-source-child"),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();

        unpack_prefab_instance(&mut world, root);
        let local_actor_id = world.get::<ActorId>(generated).unwrap().0.clone();
        assert!(world.get::<ChildOf>(generated).is_none());

        apply_command_undo(&mut world);
        apply_command_redo(&mut world);

        let restored = world
            .query::<(Entity, &ActorId)>()
            .iter(&world)
            .find_map(|(entity, actor_id)| (actor_id.0 == local_actor_id).then_some(entity))
            .expect("redo should restore the committed unparented root");
        assert!(world.get::<ChildOf>(restored).is_none());
    }

    #[test]
    fn unpack_layer_preserves_nested_prefab_links() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let base_material = MaterialDesc::default();
        let mut overridden_material = base_material.clone();
        overridden_material.roughness = 0.21;
        let mut outer_instance = PrefabInstance::new("outer", "assets/prefabs/outer.scn.ron");
        outer_instance.overrides_ron = shared::encode_prefab_overrides(&shared::PrefabOverrides {
            properties: vec![shared::PrefabPropertyOverride {
                target: shared::PrefabActorPath {
                    instance_chain: vec!["nested-anchor".to_string()],
                    actor_id: "nested-child".to_string(),
                },
                component_type: std::any::type_name::<MaterialDesc>().to_string(),
                property_path: "roughness".to_string(),
                base_component_ron: ron::to_string(&base_material).unwrap(),
                value_component_ron: ron::to_string(&overridden_material).unwrap(),
            }],
            ..default()
        })
        .unwrap();
        let root = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                outer_instance,
                PrefabRef::new("assets/prefabs/outer.scn.ron"),
                HydratedPrefabReady,
            ))
            .id();
        let nested = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                ActorId::new("nested-anchor"),
                PrefabInstance::new("nested", "assets/prefabs/nested.scn.ron"),
                PrefabRef::new("assets/prefabs/nested.scn.ron"),
                HydratedPrefabReady,
                ChildOf(root),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();
        let nested_child = world
            .spawn((
                LevelObject,
                ActorId::new("nested-child"),
                overridden_material,
                ChildOf(nested),
                HydratedPrefabMember {
                    instance_root: nested,
                },
            ))
            .id();

        unpack_prefab_instance(&mut world, root);

        assert!(world.get::<PrefabInstance>(root).is_none());
        assert!(world.get::<PrefabInstance>(nested).is_some());
        assert!(world.get::<HydratedPrefabMember>(nested).is_none());
        assert_eq!(
            world
                .get::<HydratedPrefabMember>(nested_child)
                .map(|owner| owner.instance_root),
            Some(nested)
        );
        let nested_overrides =
            shared::decode_prefab_overrides(world.get::<PrefabInstance>(nested).unwrap()).unwrap();
        assert_eq!(nested_overrides.properties.len(), 1);
        assert!(nested_overrides.properties[0]
            .target
            .instance_chain
            .is_empty());
        assert_eq!(
            ron::from_str::<MaterialDesc>(&nested_overrides.properties[0].value_component_ron)
                .unwrap()
                .roughness,
            0.21
        );
    }

    #[test]
    fn complete_prefab_conversion_is_recursive_unique_and_undoable() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let outer_instance = PrefabInstance::new("outer", "assets/prefabs/outer.scn.ron");
        let root = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                outer_instance.clone(),
                PrefabRef::new(outer_instance.source_path.clone()),
                HydratedPrefabReady,
            ))
            .id();
        world.spawn((
            LevelObject,
            ActorId::new("repeated-source-id"),
            Name::new("Outer Child"),
            ChildOf(root),
            HydratedPrefabMember {
                instance_root: root,
            },
        ));
        let nested = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                ActorId::new("nested-anchor"),
                Name::new("Nested Anchor"),
                PrefabInstance::new("nested", "assets/prefabs/nested.scn.ron"),
                PrefabRef::new("assets/prefabs/nested.scn.ron"),
                HydratedPrefabReady,
                ChildOf(root),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();
        world.spawn((
            LevelObject,
            ActorId::new("repeated-source-id"),
            Name::new("Nested Child"),
            ChildOf(nested),
            HydratedPrefabMember {
                instance_root: nested,
            },
        ));
        let local_nested = world
            .spawn((
                LevelObject,
                ActorKind::PrefabAnchor,
                ActorId::new("authored-local-anchor"),
                Name::new("Authored Local Nested"),
                PrefabInstance::new("local-nested", "assets/prefabs/local.scn.ron"),
                PrefabRef::new("assets/prefabs/local.scn.ron"),
                HydratedPrefabReady,
                ChildOf(root),
            ))
            .id();
        world.spawn((
            LevelObject,
            ActorId::new("repeated-source-id"),
            Name::new("Authored Local Child"),
            ChildOf(local_nested),
            HydratedPrefabMember {
                instance_root: local_nested,
            },
        ));

        convert_prefab_completely_to_local(&mut world, root);

        let mut prefab_instances = world.query::<&PrefabInstance>();
        assert_eq!(prefab_instances.iter(&world).count(), 0);
        let mut generated_members = world.query::<&HydratedPrefabMember>();
        assert_eq!(generated_members.iter(&world).count(), 0);
        let ids: Vec<String> = world
            .query_filtered::<&ActorId, With<LevelObject>>()
            .iter(&world)
            .map(|actor_id| actor_id.0.clone())
            .collect();
        let unique: HashSet<_> = ids.iter().collect();
        assert_eq!(ids.len(), unique.len());
        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 1);

        apply_command_undo(&mut world);
        assert_eq!(world.get::<PrefabInstance>(root), Some(&outer_instance));
        let mut prefab_instances = world.query::<(Entity, &PrefabInstance)>();
        let restored_instances: Vec<_> = prefab_instances.iter(&world).collect();
        assert_eq!(restored_instances.len(), 2);
        let restored_local = world
            .query::<(Entity, &Name, &PrefabInstance)>()
            .iter(&world)
            .find_map(|(entity, name, _)| {
                (name.as_str() == "Authored Local Nested").then_some(entity)
            })
            .expect("undo should restore authored local nested links");
        assert_eq!(
            world.get::<ChildOf>(restored_local).map(ChildOf::parent),
            Some(root)
        );

        apply_command_redo(&mut world);
        assert!(world.get::<PrefabInstance>(root).is_none());
        let mut prefab_instances = world.query::<&PrefabInstance>();
        assert_eq!(prefab_instances.iter(&world).count(), 0);
        let ids: Vec<String> = world
            .query_filtered::<&ActorId, With<LevelObject>>()
            .iter(&world)
            .map(|actor_id| actor_id.0.clone())
            .collect();
        let unique: HashSet<_> = ids.iter().collect();
        assert_eq!(ids.len(), unique.len());
    }

    #[test]
    fn unpack_waits_for_source_readiness() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let root = world
            .spawn((
                LevelObject,
                PrefabInstance::new("asset", "assets/prefabs/missing.scn.ron"),
                PrefabRef::new("assets/prefabs/missing.scn.ron"),
            ))
            .id();

        unpack_prefab_instance(&mut world, root);

        assert!(world.get::<PrefabInstance>(root).is_some());
        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 0);
        assert!(world.resource::<SceneIo>().status.contains("still loading"));
    }

    #[test]
    fn duplicate_remaps_actor_ids_and_excludes_generated_members() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        world.init_resource::<SelectedEntity>();
        let root = world
            .spawn((
                LevelObject,
                ActorId::new("root"),
                PrefabInstance::new("asset", "assets/prefabs/test.scn.ron"),
            ))
            .id();
        world.spawn((
            LevelObject,
            ActorId::new("generated"),
            ChildOf(root),
            HydratedPrefabMember {
                instance_root: root,
            },
        ));
        world.spawn((LevelObject, ActorId::new("local"), ChildOf(root)));

        duplicate_entities_with_history(&mut world, &[root]);

        let ids: Vec<String> = world
            .query_filtered::<&ActorId, With<LevelObject>>()
            .iter(&world)
            .map(|id| id.0.clone())
            .collect();
        let unique: HashSet<_> = ids.iter().collect();
        assert_eq!(ids.len(), 5, "generated source content must not be copied");
        assert_eq!(unique.len(), ids.len(), "duplicated actors need fresh IDs");
    }

    #[test]
    fn duplicate_navigation_bounds_gets_actor_owned_artifact_path() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        world.init_resource::<SelectedEntity>();
        let original_id = ActorId::new("navigation-bounds-original");
        let original_bounds = NavigationBounds::for_actor(&original_id.0);
        let original = world
            .spawn((
                LevelObject,
                ActorKind::Navigation,
                original_id,
                Transform::IDENTITY,
                original_bounds.clone(),
            ))
            .id();

        duplicate_entities_with_history(&mut world, &[original]);

        let (duplicate_id, duplicate_bounds) = world
            .query::<(Entity, &ActorId, &NavigationBounds)>()
            .iter(&world)
            .find_map(|(entity, actor_id, bounds)| {
                (entity != original).then_some((actor_id.clone(), bounds.clone()))
            })
            .expect("duplicated navigation bounds should exist");
        assert_ne!(
            duplicate_bounds.artifact_path,
            original_bounds.artifact_path
        );
        assert_eq!(
            duplicate_bounds.artifact_path,
            shared::navigation_artifact_path_for_actor(&duplicate_id.0)
        );

        apply_command_undo(&mut world);
        apply_command_redo(&mut world);
        let redone = world
            .query::<(&ActorId, &NavigationBounds)>()
            .iter(&world)
            .find(|(actor_id, _)| **actor_id == duplicate_id)
            .expect("redo should preserve the duplicate actor identity");
        assert_eq!(redone.1, &duplicate_bounds);
    }

    #[test]
    fn existing_navigation_snapshot_keeps_deliberate_generated_path() {
        let mut world = World::new();
        let mut bounds = NavigationBounds::for_actor("bounds-existing");
        bounds.artifact_path = "assets/navigation/generated/custom-zone.nav.ron".into();
        let entity = world
            .spawn((
                LevelObject,
                ActorKind::Navigation,
                ActorId::new("bounds-existing"),
                Transform::IDENTITY,
                bounds.clone(),
            ))
            .id();
        let mut snapshot = snapshot_entity(&world, entity).unwrap();

        assign_missing_actor_ids(&mut snapshot);

        assert_eq!(snapshot.navigation_bounds.as_ref(), Some(&bounds));
    }

    #[test]
    fn linked_members_reject_direct_history_mutations() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        world.init_resource::<SelectedEntity>();
        let root = world.spawn(LevelObject).id();
        let member = world
            .spawn((
                LevelObject,
                Name::new("Source Actor"),
                Transform::default(),
                HydratedPrefabMember {
                    instance_root: root,
                },
            ))
            .id();

        rename_entity_with_history(&mut world, member, "Mutated".to_string());
        set_transform_with_history(
            &mut world,
            member,
            Transform::default(),
            Transform::from_xyz(9.0, 0.0, 0.0),
        );
        set_editor_visibility_with_history(&mut world, member, EditorVisibility { visible: false });
        delete_entities_with_history(&mut world, &[member]);

        assert_eq!(world.get::<Name>(member).unwrap().as_str(), "Source Actor");
        assert_eq!(
            world.get::<Transform>(member).unwrap(),
            &Transform::default()
        );
        assert!(world.get_entity(member).is_ok());
        assert_eq!(world.resource::<EditorHistory>().undo_depth(), 0);
    }

    #[test]
    fn undoing_prefab_relink_restores_broken_hydration_guard() {
        let mut world = World::new();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let broken = PrefabInstance::new("broken", "assets/prefabs/missing.scn.ron");
        let root = world
            .spawn((
                LevelObject,
                broken.clone(),
                PrefabHydrationBlocked {
                    reason: "missing prefab source".to_string(),
                },
            ))
            .id();
        let old = capture_prefab_edit_state(&world, root);
        let valid = PrefabInstance::new("valid", "assets/prefabs/valid.scn.ron");
        world
            .entity_mut(root)
            .remove::<PrefabHydrationBlocked>()
            .insert((valid.clone(), PrefabRef::new(valid.source_path.clone())));
        record_prefab_edit_state(&mut world, root, old);

        apply_command_undo(&mut world);

        assert_eq!(world.get::<PrefabInstance>(root), Some(&broken));
        assert!(world.get::<PrefabRef>(root).is_none());
        assert!(world
            .get::<PrefabHydrationBlocked>(root)
            .is_some_and(|blocked| blocked.reason == "missing prefab source"));
        assert!(world.get::<DynamicWorldRoot>(root).is_none());
        assert!(world.get::<WorldInstance>(root).is_none());
    }
}
