//! Modal terrain height sculpting with one history transaction per pointer stroke.

use bevy::prelude::*;
use bevy_egui::EguiContexts;
use shared::{TerrainDesc, AUTHORING_COMPONENT_TERRAIN, COMPONENT_TERRAIN_DESC};

use crate::camera::EditorCamera;
use crate::history::{reflected_component_transaction, EditorHistory};
use crate::operators::{ActiveOperator, OperatorPhase, OperatorStatus};
use crate::scene_io::SceneIo;
use crate::selection::{SelectedEntity, ViewportClick};
use crate::state::scene_tools_active;
use crate::ui::UiState;
use crate::viewport::{scene_view_ray, ViewportDisplayMode};

const MIN_RADIUS: f32 = 0.25;
const MAX_RADIUS: f32 = 128.0;
const MIN_STRENGTH: f32 = 0.01;
const MAX_STRENGTH: f32 = 20.0;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TerrainSculptMode {
    #[default]
    Raise,
    Lower,
    Flatten,
    Smooth,
    Noise,
}

impl TerrainSculptMode {
    pub fn label(self) -> &'static str {
        match self {
            Self::Raise => "Raise",
            Self::Lower => "Lower",
            Self::Flatten => "Flatten",
            Self::Smooth => "Smooth",
            Self::Noise => "Noise",
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub(super) struct TerrainHit {
    pub(super) entity: Entity,
    pub(super) local: Vec3,
    pub(super) world: Vec3,
    pub(super) world_rotation: Quat,
}

#[derive(Debug, Clone)]
struct TerrainStroke {
    entity: Entity,
    original: TerrainDesc,
    last_local: Vec3,
    flatten_height: f32,
    seed: u32,
}

#[derive(Resource, Debug, Clone)]
pub struct TerrainSculptState {
    pub active: bool,
    pub mode: TerrainSculptMode,
    pub radius: f32,
    /// Approximate world-space height delta per dab.
    pub strength: f32,
    hover: Option<TerrainHit>,
    stroke: Option<TerrainStroke>,
    target: Option<Entity>,
    next_seed: u32,
}

impl Default for TerrainSculptState {
    fn default() -> Self {
        Self {
            active: false,
            mode: TerrainSculptMode::Raise,
            radius: 4.0,
            strength: 0.35,
            hover: None,
            stroke: None,
            target: None,
            next_seed: 1,
        }
    }
}

impl TerrainSculptState {
    pub fn start(&mut self, target: Entity) {
        self.active = true;
        self.target = Some(target);
    }

    pub fn stop(&mut self) {
        self.active = false;
        self.hover = None;
        self.target = None;
    }

    pub fn is_stroking(&self) -> bool {
        self.stroke.is_some()
    }

    pub fn clamp_settings(&mut self) {
        self.radius = self.radius.clamp(MIN_RADIUS, MAX_RADIUS);
        self.strength = self.strength.clamp(MIN_STRENGTH, MAX_STRENGTH);
    }
}

pub struct TerrainSculptPlugin;

impl Plugin for TerrainSculptPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<TerrainSculptState>().add_systems(
            Update,
            (terrain_sculpt_input, draw_terrain_sculpt_preview)
                .chain()
                .run_if(scene_tools_active),
        );
    }
}

#[allow(clippy::too_many_arguments)]
fn terrain_sculpt_input(
    mut commands: Commands,
    mut state: ResMut<TerrainSculptState>,
    mut selected: ResMut<SelectedEntity>,
    keys: Res<ButtonInput<KeyCode>>,
    buttons: Res<ButtonInput<MouseButton>>,
    mut contexts: EguiContexts,
    ui_state: Res<UiState>,
    display: Res<ViewportDisplayMode>,
    cameras: Query<(&Camera, &GlobalTransform), With<EditorCamera>>,
    mut terrains: Query<(&GlobalTransform, &mut TerrainDesc)>,
    mut viewport_click: ResMut<ViewportClick>,
    mut scene_io: ResMut<SceneIo>,
    mut active_operator: ResMut<ActiveOperator>,
) -> Result {
    if !state.active {
        return Ok(());
    }
    viewport_click.0 = None;
    state.clamp_settings();

    let Some(entity) = state.target.or(selected.0) else {
        stop_tool_with_rollback(
            &mut state,
            &mut terrains,
            &mut scene_io,
            &mut active_operator,
            OperatorPhase::Blocked,
            "Terrain sculpt stopped: target is unavailable",
            "Target unavailable",
        );
        return Ok(());
    };
    if selected.0 != Some(entity) {
        selected.0 = Some(entity);
    }

    if display.clean_game_view {
        stop_tool_with_rollback(
            &mut state,
            &mut terrains,
            &mut scene_io,
            &mut active_operator,
            OperatorPhase::Canceled,
            "Terrain sculpt stopped for clean game view",
            "Clean game view enabled; terrain restored",
        );
        return Ok(());
    }

    let ctx = contexts.ctx_mut()?;
    let pointer_over_ui = ctx.egui_wants_pointer_input();
    let cancel_requested =
        keys.just_pressed(KeyCode::Escape) || buttons.just_pressed(MouseButton::Right);
    if cancel_requested {
        if state.is_stroking() {
            cancel_active_stroke(
                &mut state,
                &mut terrains,
                &mut scene_io,
                &mut active_operator,
            );
        } else {
            state.stop();
            scene_io.status = "Terrain sculpt tool closed".to_string();
            set_status(&mut active_operator, OperatorPhase::Canceled, "Tool closed");
        }
        return Ok(());
    }

    let Ok((global, mut terrain)) = terrains.get_mut(entity) else {
        stop_tool_with_rollback(
            &mut state,
            &mut terrains,
            &mut scene_io,
            &mut active_operator,
            OperatorPhase::Blocked,
            "Terrain sculpt stopped: target no longer exists",
            "Target no longer exists",
        );
        return Ok(());
    };

    let hit = ui_state
        .viewport_pointer_pos
        .and_then(|pointer| active_scene_ray(&cameras, pointer, ui_state.viewport_rect))
        .and_then(|ray| terrain_ray_hit(entity, &terrain, global, ray));
    state.hover = hit;

    if buttons.just_pressed(MouseButton::Left) && !pointer_over_ui {
        if let Some(hit) = state.hover.filter(|hit| hit.entity == entity) {
            let flatten_height =
                sample_height_bilinear(&terrain, hit.local.x, hit.local.z).unwrap_or_default();
            let seed = state.next_seed;
            state.next_seed = state.next_seed.wrapping_add(1).max(1);
            state.stroke = Some(TerrainStroke {
                entity,
                original: terrain.clone(),
                last_local: hit.local,
                flatten_height,
                seed,
            });
            apply_dab(
                &mut terrain,
                hit.local,
                state.radius,
                state.strength,
                state.mode,
                flatten_height,
                seed,
            );
            set_status(
                &mut active_operator,
                OperatorPhase::Preview,
                format!("{} stroke in progress", state.mode.label()),
            );
        }
    } else if buttons.pressed(MouseButton::Left) {
        let radius = state.radius;
        let strength = state.strength;
        let mode = state.mode;
        if let (Some(hit), Some(stroke)) = (state.hover, state.stroke.as_mut()) {
            if hit.entity == stroke.entity {
                let step = (radius * 0.2).max(terrain.sample_spacing * 0.25);
                let delta = hit.local - stroke.last_local;
                let distance = Vec2::new(delta.x, delta.z).length();
                if distance >= step {
                    let count = (distance / step).floor() as usize;
                    for index in 1..=count {
                        let point = stroke.last_local + delta * (index as f32 / count as f32);
                        apply_dab(
                            &mut terrain,
                            point,
                            radius,
                            strength,
                            mode,
                            stroke.flatten_height,
                            stroke.seed,
                        );
                    }
                    stroke.last_local = hit.local;
                }
            }
        }
    }

    if buttons.just_released(MouseButton::Left) {
        if let Some(stroke) = state.stroke.take() {
            let final_terrain = terrain.clone();
            let label = sculpt_history_label(state.mode);
            let mode_label = state.mode.label();
            commands.queue(move |world: &mut World| {
                finish_terrain_stroke(world, stroke, final_terrain, label, mode_label);
            });
        }
    }
    Ok(())
}

fn commit_terrain_stroke(
    world: &mut World,
    stroke: TerrainStroke,
    final_terrain: TerrainDesc,
    label: &'static str,
) -> Result<(), String> {
    let original = stroke.original;
    if let Ok(mut actor) = world.get_entity_mut(stroke.entity) {
        actor.insert(original.clone());
    }
    let result = reflected_component_transaction(
        world,
        stroke.entity,
        label,
        AUTHORING_COMPONENT_TERRAIN,
        COMPONENT_TERRAIN_DESC,
        move |world, entity| {
            world.entity_mut(entity).insert(final_terrain);
            Ok(())
        },
    );
    if result.is_err() {
        if let Ok(mut actor) = world.get_entity_mut(stroke.entity) {
            actor.insert(original);
        }
    }
    result
}

fn finish_terrain_stroke(
    world: &mut World,
    stroke: TerrainStroke,
    final_terrain: TerrainDesc,
    label: &'static str,
    mode_label: &'static str,
) {
    let undo_depth = world.resource::<EditorHistory>().undo_depth();
    match commit_terrain_stroke(world, stroke, final_terrain, label) {
        Ok(()) if world.resource::<EditorHistory>().undo_depth() > undo_depth => {
            world.resource_mut::<SceneIo>().status =
                format!("{mode_label} terrain stroke committed");
            set_status(
                &mut world.resource_mut::<ActiveOperator>(),
                OperatorPhase::Committed,
                format!("{mode_label} stroke; Ctrl+Z to undo"),
            );
        }
        Ok(()) => {
            world.resource_mut::<SceneIo>().status =
                format!("{mode_label} terrain stroke made no changes");
            set_status(
                &mut world.resource_mut::<ActiveOperator>(),
                OperatorPhase::Canceled,
                format!("{mode_label} stroke made no changes"),
            );
        }
        Err(error) => {
            world.resource_mut::<SceneIo>().status =
                format!("Terrain sculpt commit failed: {error}");
            set_status(
                &mut world.resource_mut::<ActiveOperator>(),
                OperatorPhase::Blocked,
                format!("Commit failed: {error}"),
            );
        }
    }
}

fn cancel_active_stroke(
    state: &mut TerrainSculptState,
    terrains: &mut Query<(&GlobalTransform, &mut TerrainDesc)>,
    scene_io: &mut SceneIo,
    active_operator: &mut ActiveOperator,
) {
    cancel_stroke(state, terrains);
    scene_io.status = "Terrain sculpt stroke canceled".to_string();
    set_status(
        active_operator,
        OperatorPhase::Canceled,
        "Stroke canceled; terrain restored",
    );
}

fn stop_tool_with_rollback(
    state: &mut TerrainSculptState,
    terrains: &mut Query<(&GlobalTransform, &mut TerrainDesc)>,
    scene_io: &mut SceneIo,
    active_operator: &mut ActiveOperator,
    phase: OperatorPhase,
    message: &str,
    hint: &str,
) {
    cancel_stroke(state, terrains);
    state.stop();
    scene_io.status = message.to_string();
    set_status(active_operator, phase, hint);
}

fn cancel_stroke(
    state: &mut TerrainSculptState,
    terrains: &mut Query<(&GlobalTransform, &mut TerrainDesc)>,
) {
    let Some(stroke) = state.stroke.take() else {
        return;
    };
    if let Ok((_, mut terrain)) = terrains.get_mut(stroke.entity) {
        *terrain = stroke.original;
    }
}

fn set_status(active: &mut ActiveOperator, phase: OperatorPhase, hint: impl Into<String>) {
    active.status = Some(OperatorStatus {
        id: "terrain.sculpt".to_string(),
        label: "Terrain Sculpt".to_string(),
        phase,
        hint: hint.into(),
        warnings: Vec::new(),
    });
}

fn sculpt_history_label(mode: TerrainSculptMode) -> &'static str {
    match mode {
        TerrainSculptMode::Raise => "Raise Terrain",
        TerrainSculptMode::Lower => "Lower Terrain",
        TerrainSculptMode::Flatten => "Flatten Terrain",
        TerrainSculptMode::Smooth => "Smooth Terrain",
        TerrainSculptMode::Noise => "Noise Terrain",
    }
}

pub(super) fn active_scene_ray(
    cameras: &Query<(&Camera, &GlobalTransform), With<EditorCamera>>,
    pointer: bevy_egui::egui::Pos2,
    scene_rect: bevy_egui::egui::Rect,
) -> Option<Ray3d> {
    let (camera, transform) = cameras
        .iter()
        .find(|(camera, _)| camera.is_active)
        .or_else(|| cameras.iter().next())?;
    scene_view_ray(camera, transform, pointer, scene_rect)
}

pub(super) fn terrain_ray_hit(
    entity: Entity,
    terrain: &TerrainDesc,
    global: &GlobalTransform,
    ray: Ray3d,
) -> Option<TerrainHit> {
    let world_from_local = global.affine();
    let local_from_world = world_from_local.inverse();
    let origin = local_from_world.transform_point3(ray.origin);
    let direction = local_from_world.transform_vector3(ray.direction.as_vec3());
    let local = ray_heightfield_intersection(terrain, origin, direction)?;
    Some(TerrainHit {
        entity,
        local,
        world: world_from_local.transform_point3(local),
        world_rotation: global.rotation(),
    })
}

fn ray_heightfield_intersection(
    terrain: &TerrainDesc,
    origin: Vec3,
    direction: Vec3,
) -> Option<Vec3> {
    if terrain.validate().is_err() || direction.length_squared() <= f32::EPSILON {
        return None;
    }
    let horizontal_speed = Vec2::new(direction.x, direction.z).length();
    if horizontal_speed <= 1.0e-6 {
        if direction.y.abs() <= 1.0e-6 {
            return None;
        }
        let height = sample_height_bilinear(terrain, origin.x, origin.z)?;
        let t = (height - origin.y) / direction.y;
        return (t >= 0.0).then_some(origin + direction * t);
    }
    let half_extent = (terrain.resolution - 1) as f32 * terrain.sample_spacing * 0.5;
    let (mut start, mut end) = (0.0_f32, f32::INFINITY);
    for (origin_axis, direction_axis) in [(origin.x, direction.x), (origin.z, direction.z)] {
        if direction_axis.abs() <= 1.0e-6 {
            if origin_axis < -half_extent || origin_axis > half_extent {
                return None;
            }
            continue;
        }
        let a = (-half_extent - origin_axis) / direction_axis;
        let b = (half_extent - origin_axis) / direction_axis;
        start = start.max(a.min(b));
        end = end.min(a.max(b));
    }
    if end < start || end < 0.0 {
        return None;
    }
    start = start.max(0.0);

    let span = (end - start).max(0.0);
    let steps = ((span * horizontal_speed) / (terrain.sample_spacing * 0.4))
        .ceil()
        .clamp(1.0, 8192.0) as usize;
    let surface_delta = |t: f32| {
        let point = origin + direction * t;
        sample_height_bilinear(terrain, point.x, point.z).map(|height| point.y - height)
    };
    let mut previous_t = start;
    let mut previous = surface_delta(start)?;
    if previous.abs() <= 1.0e-4 {
        return Some(origin + direction * start);
    }
    for step in 1..=steps {
        let t = start + span * (step as f32 / steps as f32);
        let current = surface_delta(t)?;
        if current.signum() != previous.signum() || current.abs() <= 1.0e-4 {
            let (mut low, mut high) = (previous_t, t);
            let low_sign = previous.signum();
            for _ in 0..14 {
                let middle = (low + high) * 0.5;
                let delta = surface_delta(middle)?;
                if delta.signum() == low_sign {
                    low = middle;
                } else {
                    high = middle;
                }
            }
            let hit_t = (low + high) * 0.5;
            return Some(origin + direction * hit_t);
        }
        previous_t = t;
        previous = current;
    }
    None
}

fn sample_height_bilinear(terrain: &TerrainDesc, local_x: f32, local_z: f32) -> Option<f32> {
    let half_extent = (terrain.resolution - 1) as f32 * terrain.sample_spacing * 0.5;
    let x = (local_x + half_extent) / terrain.sample_spacing;
    let z = (local_z + half_extent) / terrain.sample_spacing;
    let max = (terrain.resolution - 1) as f32;
    if x < 0.0 || z < 0.0 || x > max || z > max {
        return None;
    }
    let x0 = x.floor() as u32;
    let z0 = z.floor() as u32;
    let x1 = (x0 + 1).min(terrain.resolution - 1);
    let z1 = (z0 + 1).min(terrain.resolution - 1);
    let sample = |sx: u32, sz: u32| {
        terrain.heights[(sz * terrain.resolution + sx) as usize] * terrain.height_scale
    };
    let top = sample(x0, z0).lerp(sample(x1, z0), x - x0 as f32);
    let bottom = sample(x0, z1).lerp(sample(x1, z1), x - x0 as f32);
    Some(top.lerp(bottom, z - z0 as f32))
}

fn apply_dab(
    terrain: &mut TerrainDesc,
    center: Vec3,
    radius: f32,
    strength: f32,
    mode: TerrainSculptMode,
    flatten_height: f32,
    seed: u32,
) {
    let radius = radius.max(MIN_RADIUS);
    let resolution = terrain.resolution;
    let half_extent = (resolution - 1) as f32 * terrain.sample_spacing * 0.5;
    let source = terrain.heights.clone();
    let normalized_strength = strength / terrain.height_scale.max(0.01);
    for z in 0..resolution {
        for x in 0..resolution {
            let sample_position = Vec2::new(
                x as f32 * terrain.sample_spacing - half_extent,
                z as f32 * terrain.sample_spacing - half_extent,
            );
            let distance = sample_position.distance(Vec2::new(center.x, center.z));
            if distance > radius {
                continue;
            }
            let falloff = smooth_falloff(distance / radius);
            let index = (z * resolution + x) as usize;
            let current = source[index];
            let next = match mode {
                TerrainSculptMode::Raise => current + normalized_strength * falloff,
                TerrainSculptMode::Lower => current - normalized_strength * falloff,
                TerrainSculptMode::Flatten => {
                    let target = flatten_height / terrain.height_scale.max(0.01);
                    current.lerp(target, (normalized_strength * falloff).clamp(0.0, 1.0))
                }
                TerrainSculptMode::Smooth => {
                    let average = neighbor_average(&source, resolution, x, z);
                    current.lerp(average, (normalized_strength * falloff).clamp(0.0, 1.0))
                }
                TerrainSculptMode::Noise => {
                    current + signed_noise(x, z, seed) * normalized_strength * falloff
                }
            };
            terrain.heights[index] = next;
        }
    }
}

pub(super) fn smooth_falloff(normalized_distance: f32) -> f32 {
    let t = (1.0 - normalized_distance).clamp(0.0, 1.0);
    t * t * (3.0 - 2.0 * t)
}

fn neighbor_average(heights: &[f32], resolution: u32, x: u32, z: u32) -> f32 {
    let mut sum = 0.0;
    let mut count = 0;
    for dz in -1_i32..=1 {
        for dx in -1_i32..=1 {
            let sx = x as i32 + dx;
            let sz = z as i32 + dz;
            if sx >= 0 && sz >= 0 && sx < resolution as i32 && sz < resolution as i32 {
                sum += heights[(sz as u32 * resolution + sx as u32) as usize];
                count += 1;
            }
        }
    }
    sum / count as f32
}

fn signed_noise(x: u32, z: u32, seed: u32) -> f32 {
    let mut value = x
        .wrapping_mul(0x9e37_79b9)
        .wrapping_add(z.wrapping_mul(0x85eb_ca6b))
        .wrapping_add(seed.wrapping_mul(0xc2b2_ae35));
    value ^= value >> 16;
    value = value.wrapping_mul(0x7feb_352d);
    value ^= value >> 15;
    value = value.wrapping_mul(0x846c_a68b);
    value ^= value >> 16;
    (value as f32 / u32::MAX as f32) * 2.0 - 1.0
}

fn draw_terrain_sculpt_preview(state: Res<TerrainSculptState>, mut gizmos: Gizmos) {
    if !state.active {
        return;
    }
    let Some(hit) = state.hover else {
        return;
    };
    let color = match state.mode {
        TerrainSculptMode::Raise => Color::srgba(0.30, 0.92, 0.58, 0.95),
        TerrainSculptMode::Lower => Color::srgba(0.96, 0.36, 0.32, 0.95),
        TerrainSculptMode::Flatten => Color::srgba(0.32, 0.72, 1.0, 0.95),
        TerrainSculptMode::Smooth => Color::srgba(0.72, 0.56, 1.0, 0.95),
        TerrainSculptMode::Noise => Color::srgba(1.0, 0.76, 0.28, 0.95),
    };
    let rotation = hit.world_rotation * Quat::from_rotation_x(std::f32::consts::FRAC_PI_2);
    gizmos
        .circle(
            Isometry3d::new(hit.world + Vec3::Y * 0.03, rotation),
            state.radius,
            color,
        )
        .resolution(48);
    gizmos.sphere(
        Isometry3d::from_translation(hit.world + Vec3::Y * 0.035),
        0.06,
        color,
    );
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::history::EditorHistory;
    use crate::operators::test_harness::{assert_undo_redo_round_trip, OperatorInvariantHarness};
    use shared::{ActorKind, LevelObject};

    fn sample(terrain: &TerrainDesc, x: u32, z: u32) -> f32 {
        terrain.heights[(z * terrain.resolution + x) as usize]
    }

    #[test]
    fn vertical_ray_hits_heightfield_surface() {
        let mut terrain = TerrainDesc::flat(3);
        terrain.height_scale = 2.0;
        terrain.heights[4] = 1.0;
        let hit = ray_heightfield_intersection(&terrain, Vec3::new(0.0, 5.0, 0.0), Vec3::NEG_Y)
            .expect("ray should hit center sample");
        assert!((hit.y - 2.0).abs() < 0.001);
    }

    #[test]
    fn raise_and_lower_only_touch_brush_footprint() {
        let mut terrain = TerrainDesc::flat(5);
        apply_dab(
            &mut terrain,
            Vec3::ZERO,
            1.1,
            1.0,
            TerrainSculptMode::Raise,
            0.0,
            1,
        );
        assert!(sample(&terrain, 2, 2) > 0.0);
        assert_eq!(sample(&terrain, 0, 0), 0.0);
        let raised = sample(&terrain, 2, 2);
        apply_dab(
            &mut terrain,
            Vec3::ZERO,
            1.1,
            1.0,
            TerrainSculptMode::Lower,
            0.0,
            1,
        );
        assert!(sample(&terrain, 2, 2) < raised);
    }

    #[test]
    fn flatten_and_smooth_converge_toward_targets() {
        let mut terrain = TerrainDesc::flat(3);
        terrain.height_scale = 1.0;
        terrain.heights[4] = 4.0;
        apply_dab(
            &mut terrain,
            Vec3::ZERO,
            2.0,
            1.0,
            TerrainSculptMode::Smooth,
            0.0,
            2,
        );
        assert!(sample(&terrain, 1, 1) < 4.0);
        apply_dab(
            &mut terrain,
            Vec3::ZERO,
            2.0,
            0.5,
            TerrainSculptMode::Flatten,
            2.0,
            2,
        );
        assert!(sample(&terrain, 1, 1) > 0.0);
    }

    #[test]
    fn noise_is_deterministic_for_stroke_seed() {
        let mut first = TerrainDesc::flat(5);
        let mut second = first.clone();
        for terrain in [&mut first, &mut second] {
            apply_dab(
                terrain,
                Vec3::ZERO,
                3.0,
                0.4,
                TerrainSculptMode::Noise,
                0.0,
                42,
            );
        }
        assert_eq!(first.heights, second.heights);
        assert!(first.heights.iter().any(|height| *height != 0.0));
    }

    #[test]
    fn many_dabs_commit_as_one_undoable_stroke() {
        let mut app = App::new();
        app.register_type::<ActorKind>()
            .register_type::<TerrainDesc>();
        let world = app.world_mut();
        world.init_resource::<ActiveOperator>();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        world.init_resource::<TerrainSculptState>();
        let original = TerrainDesc::flat(9);
        let entity = world
            .spawn((LevelObject, ActorKind::Terrain, original.clone()))
            .id();
        let mut final_terrain = original.clone();
        for x in [-2.0, 0.0, 2.0] {
            apply_dab(
                &mut final_terrain,
                Vec3::new(x, 0.0, 0.0),
                2.5,
                0.4,
                TerrainSculptMode::Raise,
                0.0,
                7,
            );
        }
        world.entity_mut(entity).insert(final_terrain.clone());
        let stroke = TerrainStroke {
            entity,
            original: original.clone(),
            last_local: Vec3::ZERO,
            flatten_height: 0.0,
            seed: 7,
        };
        {
            let mut state = world.resource_mut::<TerrainSculptState>();
            state.start(entity);
            state.stroke = Some(stroke);
        }
        let stroke = world
            .resource_mut::<TerrainSculptState>()
            .stroke
            .take()
            .unwrap();
        let harness = OperatorInvariantHarness::capture(world);

        finish_terrain_stroke(
            world,
            stroke,
            final_terrain.clone(),
            "Raise Terrain",
            "Raise",
        );

        harness.assert_committed(world, 1, 0);
        harness.assert_status(world, "terrain.sculpt", OperatorPhase::Committed);
        assert_eq!(world.get::<TerrainDesc>(entity), Some(&final_terrain));
        let state = world.resource::<TerrainSculptState>();
        assert!(state.active);
        assert_eq!(state.target, Some(entity));
        assert!(!state.is_stroking());
        assert_undo_redo_round_trip(world, original, final_terrain, |world| {
            world.get::<TerrainDesc>(entity).unwrap().clone()
        });
    }

    #[test]
    fn no_op_stroke_terminates_without_dirtying_or_history() {
        let mut app = App::new();
        app.register_type::<ActorKind>()
            .register_type::<TerrainDesc>();
        let world = app.world_mut();
        world.init_resource::<ActiveOperator>();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let original = TerrainDesc::flat(5);
        let entity = world
            .spawn((LevelObject, ActorKind::Terrain, original.clone()))
            .id();
        let harness = OperatorInvariantHarness::capture(world);

        finish_terrain_stroke(
            world,
            TerrainStroke {
                entity,
                original: original.clone(),
                last_local: Vec3::ZERO,
                flatten_height: 0.0,
                seed: 1,
            },
            original.clone(),
            "Smooth Terrain",
            "Smooth",
        );

        harness.assert_canceled(world);
        harness.assert_status(world, "terrain.sculpt", OperatorPhase::Canceled);
        assert_eq!(world.get::<TerrainDesc>(entity), Some(&original));
    }

    #[test]
    fn commit_failure_restores_preview_and_reports_blocked_without_history() {
        let mut app = App::new();
        let world = app.world_mut();
        world.init_resource::<ActiveOperator>();
        world.init_resource::<EditorHistory>();
        world.init_resource::<SceneIo>();
        let original = TerrainDesc::flat(5);
        let mut preview = original.clone();
        apply_dab(
            &mut preview,
            Vec3::ZERO,
            3.0,
            0.8,
            TerrainSculptMode::Raise,
            0.0,
            11,
        );
        let entity = world
            .spawn((LevelObject, ActorKind::Terrain, preview.clone()))
            .id();
        let mut hierarchy = crate::ui::hierarchy_state::HierarchyPanelState::default();
        hierarchy.locked.insert(entity);
        world.insert_resource(hierarchy);
        let harness = OperatorInvariantHarness::capture(world);

        finish_terrain_stroke(
            world,
            TerrainStroke {
                entity,
                original: original.clone(),
                last_local: Vec3::ZERO,
                flatten_height: 0.0,
                seed: 11,
            },
            preview,
            "Raise Terrain",
            "Raise",
        );

        harness.assert_blocked(world);
        harness.assert_status(world, "terrain.sculpt", OperatorPhase::Blocked);
        assert_eq!(world.get::<TerrainDesc>(entity), Some(&original));
        assert!(world
            .resource::<SceneIo>()
            .status
            .starts_with("Terrain sculpt commit failed:"));
    }

    #[test]
    fn cancel_restores_exact_pre_stroke_descriptor() {
        fn cancel_once(
            mut state: ResMut<TerrainSculptState>,
            mut terrains: Query<(&GlobalTransform, &mut TerrainDesc)>,
            mut scene_io: ResMut<SceneIo>,
            mut active_operator: ResMut<ActiveOperator>,
        ) {
            cancel_active_stroke(
                &mut state,
                &mut terrains,
                &mut scene_io,
                &mut active_operator,
            );
        }

        let mut app = App::new();
        app.init_resource::<ActiveOperator>()
            .init_resource::<EditorHistory>()
            .init_resource::<SceneIo>();
        let original = TerrainDesc::flat(5);
        let mut preview = original.clone();
        apply_dab(
            &mut preview,
            Vec3::ZERO,
            3.0,
            0.8,
            TerrainSculptMode::Raise,
            0.0,
            11,
        );
        let entity = app
            .world_mut()
            .spawn((LevelObject, GlobalTransform::default(), preview))
            .id();
        let mut state = TerrainSculptState::default();
        state.start(entity);
        state.stroke = Some(TerrainStroke {
            entity,
            original: original.clone(),
            last_local: Vec3::ZERO,
            flatten_height: 0.0,
            seed: 11,
        });
        app.insert_resource(state).add_systems(Update, cancel_once);
        let harness = OperatorInvariantHarness::capture(app.world_mut());

        app.update();
        harness.assert_canceled(app.world_mut());
        harness.assert_status(app.world(), "terrain.sculpt", OperatorPhase::Canceled);
        assert_eq!(app.world().get::<TerrainDesc>(entity), Some(&original));
        let state = app.world().resource::<TerrainSculptState>();
        assert!(state.active);
        assert_eq!(state.target, Some(entity));
        assert!(!state.is_stroking());
    }

    #[test]
    fn clean_view_interruption_restores_stroke_and_terminates_tool() {
        fn interrupt_once(
            mut state: ResMut<TerrainSculptState>,
            mut terrains: Query<(&GlobalTransform, &mut TerrainDesc)>,
            mut scene_io: ResMut<SceneIo>,
            mut active_operator: ResMut<ActiveOperator>,
        ) {
            stop_tool_with_rollback(
                &mut state,
                &mut terrains,
                &mut scene_io,
                &mut active_operator,
                OperatorPhase::Canceled,
                "Terrain sculpt stopped for clean game view",
                "Clean game view enabled; terrain restored",
            );
        }

        let mut app = App::new();
        app.init_resource::<ActiveOperator>()
            .init_resource::<EditorHistory>()
            .init_resource::<SceneIo>();
        let original = TerrainDesc::flat(5);
        let mut preview = original.clone();
        apply_dab(
            &mut preview,
            Vec3::ZERO,
            3.0,
            0.8,
            TerrainSculptMode::Raise,
            0.0,
            11,
        );
        let entity = app
            .world_mut()
            .spawn((LevelObject, GlobalTransform::default(), preview))
            .id();
        let mut state = TerrainSculptState::default();
        state.start(entity);
        state.stroke = Some(TerrainStroke {
            entity,
            original: original.clone(),
            last_local: Vec3::ZERO,
            flatten_height: 0.0,
            seed: 11,
        });
        app.insert_resource(state)
            .add_systems(Update, interrupt_once);
        let harness = OperatorInvariantHarness::capture(app.world_mut());

        app.update();

        harness.assert_canceled(app.world_mut());
        harness.assert_status(app.world(), "terrain.sculpt", OperatorPhase::Canceled);
        assert_eq!(app.world().get::<TerrainDesc>(entity), Some(&original));
        assert!(!app.world().resource::<TerrainSculptState>().active);
    }
}
