//! Guarded editor shutdown and the handoff to scene persistence.

use bevy::app::{AppExit, MainScheduleOrder};
use bevy::ecs::schedule::ScheduleLabel;
use bevy::prelude::*;
use bevy::window::{PrimaryWindow, WindowCloseRequested};

use crate::native_dialog::NativeDialogBroker;
use crate::scene_io::SceneIo;

const SAVE_ALL_LABEL: &str = "Save All";
const DISCARD_LABEL: &str = "Discard";
const CANCEL_LABEL: &str = "Cancel";

/// The user or editor surface that initiated a guarded shutdown.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShutdownSource {
    NativeWindow,
    FileMenu,
    Programmatic,
    SwitchProject,
}

/// Observable state of the single editor shutdown flow.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ShutdownIntent {
    #[default]
    Idle,
    WaitingForBroker {
        source: ShutdownSource,
    },
    Confirming {
        source: ShutdownSource,
    },
    Saving {
        source: ShutdownSource,
    },
    Authorized {
        source: ShutdownSource,
        require_clean_scenes: bool,
    },
    ExitSent {
        source: ShutdownSource,
    },
}

impl ShutdownIntent {
    pub fn source(self) -> Option<ShutdownSource> {
        match self {
            Self::Idle => None,
            Self::WaitingForBroker { source }
            | Self::Confirming { source }
            | Self::Saving { source }
            | Self::Authorized { source, .. }
            | Self::ExitSent { source } => Some(source),
        }
    }
}

/// Coordinates all editor exit requests so only an authorized flow emits [`AppExit`].
#[derive(Resource, Debug, Default)]
pub struct ShutdownCoordinator {
    intent: ShutdownIntent,
    coalesced_requests: u32,
    last_blocker: Option<String>,
}

impl ShutdownCoordinator {
    pub fn intent(&self) -> ShutdownIntent {
        self.intent
    }

    pub fn coalesced_requests(&self) -> u32 {
        self.coalesced_requests
    }

    pub fn last_blocker(&self) -> Option<&str> {
        self.last_blocker.as_deref()
    }

    pub fn authorized_exit_sent(&self) -> bool {
        matches!(self.intent, ShutdownIntent::ExitSent { .. })
    }

    /// Queue a shutdown if no shutdown flow is already active.
    ///
    /// Repeated window, menu, or programmatic requests are coalesced until the
    /// current flow is cancelled, fails to save, or sends the authorized exit.
    pub fn request(&mut self, source: ShutdownSource) -> bool {
        if self.intent != ShutdownIntent::Idle {
            self.coalesced_requests = self.coalesced_requests.saturating_add(1);
            return false;
        }
        self.intent = ShutdownIntent::WaitingForBroker { source };
        self.coalesced_requests = 0;
        self.last_blocker = None;
        true
    }

    #[cfg(test)]
    pub(crate) fn set_intent_for_test(&mut self, intent: ShutdownIntent) {
        self.intent = intent;
    }
}

#[cfg(test)]
pub(crate) fn arm_shutdown_save_for_test(world: &mut World) {
    world.init_resource::<ShutdownCoordinator>();
    world
        .resource_mut::<ShutdownCoordinator>()
        .set_intent_for_test(ShutdownIntent::Saving {
            source: ShutdownSource::Programmatic,
        });
}

/// Emitted once when the guarded dialog chooses `Save All`.
///
/// Scene persistence owns the actual save sequence and must finish it with
/// [`complete_shutdown_save`].
#[derive(Message, Debug, Clone, Copy, PartialEq, Eq)]
pub struct ShutdownSaveAllRequested {
    pub source: ShutdownSource,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ShutdownSaveOutcome {
    Saved,
    Cancelled,
    Failed(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShutdownDecision {
    SaveAll,
    Discard,
    Cancel,
}

/// Runs after Bevy's `Last` schedule so no editor authoring system can mutate
/// a document after the final dirty-state check in the same frame.
#[derive(ScheduleLabel, Debug, Clone, PartialEq, Eq, Hash)]
pub struct EditorShutdownFinalize;

/// Queue a guarded shutdown from an exclusive-world UI or workflow callback.
pub fn request_editor_shutdown(world: &mut World, source: ShutdownSource) -> bool {
    world
        .get_resource_mut::<ShutdownCoordinator>()
        .is_some_and(|mut coordinator| coordinator.request(source))
}

/// Queue a project-browser handoff through the same dirty-document guard as editor exit.
pub fn request_project_switch(world: &mut World) -> bool {
    request_editor_shutdown(world, ShutdownSource::SwitchProject)
}

/// Resolve the dirty-scene confirmation through the shared coordinator.
pub fn resolve_shutdown_decision(world: &mut World, decision: ShutdownDecision) -> bool {
    let (save_request, discard_assets, status) = {
        let Some(mut coordinator) = world.get_resource_mut::<ShutdownCoordinator>() else {
            return false;
        };
        let ShutdownIntent::Confirming { source } = coordinator.intent else {
            return false;
        };
        match decision {
            ShutdownDecision::SaveAll => {
                coordinator.intent = ShutdownIntent::Saving { source };
                (
                    Some(source),
                    false,
                    Some("Saving all modified scene tabs before shutdown"),
                )
            }
            ShutdownDecision::Discard => {
                coordinator.intent = ShutdownIntent::Authorized {
                    source,
                    require_clean_scenes: false,
                };
                (None, true, None)
            }
            ShutdownDecision::Cancel => {
                coordinator.intent = ShutdownIntent::Idle;
                (
                    None,
                    false,
                    Some("Shutdown cancelled; unsaved scene tabs retained"),
                )
            }
        }
    };

    if discard_assets {
        crate::asset_documents::discard_all_authored_assets(world);
    }

    if let Some(status) = status {
        if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
            scene_io.set_status(status);
        }
    }
    if let Some(source) = save_request {
        if let Err(error) = crate::settings_ui::save_project_settings_if_dirty(world) {
            complete_shutdown_save(world, ShutdownSaveOutcome::Failed(error));
            return true;
        }
        if let Err(error) = crate::asset_documents::save_all_authored_assets(world) {
            complete_shutdown_save(world, ShutdownSaveOutcome::Failed(error));
            return true;
        }
        world.write_message(ShutdownSaveAllRequested { source });
    }
    true
}

/// Finish the save-all handoff. A reported success is authorized only after
/// every scene tab is observably clean.
pub fn complete_shutdown_save(world: &mut World, outcome: ShutdownSaveOutcome) -> bool {
    let documents_are_clean = !scene_has_unsaved_changes(world)
        && !asset_documents_have_unsaved_changes(world)
        && !project_settings_have_unsaved_changes(world);
    let Some(mut coordinator) = world.get_resource_mut::<ShutdownCoordinator>() else {
        return false;
    };
    let ShutdownIntent::Saving { source } = coordinator.intent else {
        return false;
    };

    match outcome {
        ShutdownSaveOutcome::Saved if documents_are_clean => {
            coordinator.intent = ShutdownIntent::Authorized {
                source,
                require_clean_scenes: true,
            };
            coordinator.last_blocker = None;
            true
        }
        ShutdownSaveOutcome::Saved => {
            coordinator.intent = ShutdownIntent::Idle;
            coordinator.last_blocker = Some(
                "Save All completed while unsaved scenes, assets, or project settings remain"
                    .into(),
            );
            false
        }
        ShutdownSaveOutcome::Cancelled => {
            coordinator.intent = ShutdownIntent::Idle;
            coordinator.last_blocker = None;
            false
        }
        ShutdownSaveOutcome::Failed(error) => {
            coordinator.intent = ShutdownIntent::Idle;
            coordinator.last_blocker = Some(error);
            false
        }
    }
}

pub struct ShutdownPlugin;

impl Plugin for ShutdownPlugin {
    fn build(&self, app: &mut App) {
        app.init_schedule(EditorShutdownFinalize);
        app.world_mut()
            .resource_mut::<MainScheduleOrder>()
            .insert_after(Last, EditorShutdownFinalize);
        app.init_resource::<ShutdownCoordinator>()
            .add_message::<ShutdownSaveAllRequested>()
            .add_systems(
                Update,
                (capture_native_close_requests, drive_shutdown).chain(),
            )
            .add_systems(EditorShutdownFinalize, finalize_authorized_shutdown);
    }
}

fn capture_native_close_requests(
    mut commands: Commands,
    mut close_requests: MessageReader<WindowCloseRequested>,
    primary_window: Query<Entity, With<PrimaryWindow>>,
    mut coordinator: ResMut<ShutdownCoordinator>,
) {
    let Ok(primary_window) = primary_window.single() else {
        return;
    };
    for request in close_requests.read() {
        if request.window == primary_window {
            coordinator.request(ShutdownSource::NativeWindow);
        } else {
            commands.entity(request.window).despawn();
        }
    }
}

fn drive_shutdown(world: &mut World) {
    let intent = world.resource::<ShutdownCoordinator>().intent;
    match intent {
        ShutdownIntent::Idle
        | ShutdownIntent::Confirming { .. }
        | ShutdownIntent::Saving { .. }
        | ShutdownIntent::ExitSent { .. } => {}
        ShutdownIntent::WaitingForBroker { source } => {
            let unsaved_count = unsaved_document_count(world);
            if unsaved_count == 0 {
                world.resource_mut::<ShutdownCoordinator>().intent = ShutdownIntent::Authorized {
                    source,
                    require_clean_scenes: true,
                };
                return;
            }
            if world.resource::<NativeDialogBroker>().is_pending() {
                return;
            }

            let action = if source == ShutdownSource::SwitchProject {
                "switching projects"
            } else {
                "quitting"
            };
            let description = if unsaved_count == 1 {
                format!("One editor document has unsaved changes. Save it before {action}?")
            } else {
                format!(
                    "{unsaved_count} editor documents have unsaved changes. Save all before {action}?"
                )
            };
            let title = if source == ShutdownSource::SwitchProject {
                "Switch Project"
            } else {
                "Quit Blacksite Editor"
            };
            let request = world.resource::<NativeDialogBroker>().request(
                move || {
                    rfd::MessageDialog::new()
                        .set_title(title)
                        .set_description(description)
                        .set_level(rfd::MessageLevel::Warning)
                        .set_buttons(rfd::MessageButtons::YesNoCancelCustom(
                            SAVE_ALL_LABEL.into(),
                            DISCARD_LABEL.into(),
                            CANCEL_LABEL.into(),
                        ))
                        .show()
                },
                |world, result| {
                    resolve_shutdown_decision(world, decision_from_dialog(result));
                },
            );
            if request.is_ok() {
                world.resource_mut::<ShutdownCoordinator>().intent =
                    ShutdownIntent::Confirming { source };
            }
        }
        ShutdownIntent::Authorized { .. } => {}
    }
}

/// Final dirty-state recheck after editor UI and authoring schedules have completed.
pub(crate) fn finalize_authorized_shutdown(world: &mut World) {
    let (source, require_clean_scenes) = {
        let coordinator = world.resource::<ShutdownCoordinator>();
        let ShutdownIntent::Authorized {
            source,
            require_clean_scenes,
        } = coordinator.intent
        else {
            return;
        };
        (source, require_clean_scenes)
    };
    if require_clean_scenes
        && (scene_has_unsaved_changes(world)
            || asset_documents_have_unsaved_changes(world)
            || project_settings_have_unsaved_changes(world))
    {
        world.resource_mut::<ShutdownCoordinator>().intent =
            ShutdownIntent::WaitingForBroker { source };
        return;
    }
    if source == ShutdownSource::SwitchProject {
        if let Err(error) = crate::launcher::spawn_project_launcher_process() {
            let message = format!("Switch project failed: {error}");
            {
                let mut coordinator = world.resource_mut::<ShutdownCoordinator>();
                coordinator.intent = ShutdownIntent::Idle;
                coordinator.last_blocker = Some(message.clone());
            }
            if let Some(mut scene_io) = world.get_resource_mut::<SceneIo>() {
                scene_io.set_status(message);
            }
            return;
        }
    }
    world.resource_mut::<ShutdownCoordinator>().intent = ShutdownIntent::ExitSent { source };
    debug!(?source, "Editor shutdown authorized");
    world.write_message(AppExit::Success);
}

fn scene_has_unsaved_changes(world: &World) -> bool {
    world
        .get_resource::<SceneIo>()
        .is_some_and(SceneIo::has_unsaved_tabs)
}

fn unsaved_scene_count(world: &World) -> usize {
    let Some(scene_io) = world.get_resource::<SceneIo>() else {
        return 0;
    };
    let tab_count = scene_io.tabs.iter().filter(|tab| tab.dirty).count();
    if scene_io.dirty
        && scene_io
            .tabs
            .get(scene_io.active_tab)
            .is_none_or(|tab| !tab.dirty)
    {
        tab_count + 1
    } else {
        tab_count
    }
}

fn unsaved_document_count(world: &World) -> usize {
    unsaved_scene_count(world)
        + world
            .get_resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
            .map_or(0, |store| store.dirty_count())
        + usize::from(project_settings_have_unsaved_changes(world))
}

fn asset_documents_have_unsaved_changes(world: &World) -> bool {
    world
        .get_resource::<crate::asset_documents::AuthoredAssetDocumentStore>()
        .is_some_and(|store| store.has_dirty_documents())
}

fn project_settings_have_unsaved_changes(world: &World) -> bool {
    world
        .get_resource::<settings::ProjectSettingsIo>()
        .is_some_and(|io| io.dirty)
}

fn decision_from_dialog(result: rfd::MessageDialogResult) -> ShutdownDecision {
    match result {
        rfd::MessageDialogResult::Custom(label) if label == SAVE_ALL_LABEL => {
            ShutdownDecision::SaveAll
        }
        rfd::MessageDialogResult::Custom(label) if label == DISCARD_LABEL => {
            ShutdownDecision::Discard
        }
        rfd::MessageDialogResult::Yes => ShutdownDecision::SaveAll,
        rfd::MessageDialogResult::No => ShutdownDecision::Discard,
        _ => ShutdownDecision::Cancel,
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc;

    use bevy::ecs::message::MessageCursor;

    use super::*;
    use crate::native_dialog::NativeDialogPlugin;

    fn confirming_world() -> World {
        let mut world = World::new();
        world.init_resource::<Messages<ShutdownSaveAllRequested>>();
        world.insert_resource(ShutdownCoordinator {
            intent: ShutdownIntent::Confirming {
                source: ShutdownSource::FileMenu,
            },
            ..default()
        });
        world
    }

    #[test]
    fn repeated_requests_are_coalesced_behind_the_first_source() {
        let mut coordinator = ShutdownCoordinator::default();

        assert!(coordinator.request(ShutdownSource::NativeWindow));
        assert!(!coordinator.request(ShutdownSource::FileMenu));
        assert_eq!(coordinator.coalesced_requests(), 1);
        assert_eq!(
            coordinator.intent(),
            ShutdownIntent::WaitingForBroker {
                source: ShutdownSource::NativeWindow
            }
        );
    }

    #[test]
    fn cancel_returns_to_idle_and_discard_authorizes_exit() {
        let mut cancel_world = confirming_world();
        assert!(resolve_shutdown_decision(
            &mut cancel_world,
            ShutdownDecision::Cancel
        ));
        assert_eq!(
            cancel_world.resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::Idle
        );

        let mut discard_world = confirming_world();
        assert!(resolve_shutdown_decision(
            &mut discard_world,
            ShutdownDecision::Discard
        ));
        assert_eq!(
            discard_world.resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::Authorized {
                source: ShutdownSource::FileMenu,
                require_clean_scenes: false,
            }
        );
    }

    #[test]
    fn dirty_cancel_preserves_the_session_and_discard_emits_one_exit() {
        let mut app = App::new();
        app.add_message::<AppExit>()
            .init_resource::<SceneIo>()
            .init_resource::<ShutdownCoordinator>();
        app.world_mut().resource_mut::<SceneIo>().mark_dirty();
        app.world_mut()
            .resource_mut::<ShutdownCoordinator>()
            .set_intent_for_test(ShutdownIntent::Confirming {
                source: ShutdownSource::Programmatic,
            });

        assert!(resolve_shutdown_decision(
            app.world_mut(),
            ShutdownDecision::Cancel
        ));
        drive_shutdown(app.world_mut());
        finalize_authorized_shutdown(app.world_mut());
        assert_eq!(app.should_exit(), None);
        assert!(app.world().resource::<SceneIo>().has_unsaved_tabs());

        app.world_mut()
            .resource_mut::<ShutdownCoordinator>()
            .set_intent_for_test(ShutdownIntent::Confirming {
                source: ShutdownSource::Programmatic,
            });
        assert!(resolve_shutdown_decision(
            app.world_mut(),
            ShutdownDecision::Discard
        ));
        drive_shutdown(app.world_mut());
        finalize_authorized_shutdown(app.world_mut());
        assert_eq!(app.should_exit(), Some(AppExit::Success));
        assert!(app.world().resource::<SceneIo>().has_unsaved_tabs());
        assert!(app
            .world()
            .resource::<ShutdownCoordinator>()
            .authorized_exit_sent());
    }

    #[test]
    fn file_menu_and_programmatic_clean_exit_use_the_same_coordinator_path() {
        for source in [ShutdownSource::FileMenu, ShutdownSource::Programmatic] {
            let mut app = App::new();
            app.add_message::<AppExit>()
                .init_resource::<SceneIo>()
                .init_resource::<ShutdownCoordinator>()
                .init_resource::<NativeDialogBroker>();

            assert!(request_editor_shutdown(app.world_mut(), source));
            drive_shutdown(app.world_mut());
            finalize_authorized_shutdown(app.world_mut());

            assert_eq!(app.should_exit(), Some(AppExit::Success));
            assert_eq!(
                app.world().resource::<ShutdownCoordinator>().intent(),
                ShutdownIntent::ExitSent { source }
            );
        }
    }

    #[test]
    fn save_all_emits_one_persistence_handoff() {
        let mut world = confirming_world();
        let mut cursor = MessageCursor::<ShutdownSaveAllRequested>::default();

        assert!(resolve_shutdown_decision(
            &mut world,
            ShutdownDecision::SaveAll
        ));
        assert!(!resolve_shutdown_decision(
            &mut world,
            ShutdownDecision::SaveAll
        ));

        let messages = world.resource::<Messages<ShutdownSaveAllRequested>>();
        let requests: Vec<_> = cursor.read(messages).copied().collect();
        assert_eq!(
            requests,
            vec![ShutdownSaveAllRequested {
                source: ShutdownSource::FileMenu
            }]
        );
    }

    #[test]
    fn clean_native_close_emits_the_authorized_exit() {
        let mut app = App::new();
        app.add_message::<WindowCloseRequested>()
            .add_plugins((NativeDialogPlugin, ShutdownPlugin))
            .init_resource::<SceneIo>();
        let window = app.world_mut().spawn(PrimaryWindow).id();
        app.world_mut()
            .write_message(WindowCloseRequested { window });

        app.update();

        assert_eq!(app.should_exit(), Some(AppExit::Success));
        assert_eq!(
            app.world().resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::ExitSent {
                source: ShutdownSource::NativeWindow
            }
        );
    }

    #[test]
    fn secondary_window_close_does_not_request_editor_shutdown() {
        let mut app = App::new();
        app.add_message::<WindowCloseRequested>()
            .add_plugins((NativeDialogPlugin, ShutdownPlugin))
            .init_resource::<SceneIo>();
        app.world_mut().spawn(PrimaryWindow);
        let secondary = app.world_mut().spawn_empty().id();
        app.world_mut()
            .write_message(WindowCloseRequested { window: secondary });

        app.update();

        assert_eq!(app.should_exit(), None);
        assert_eq!(
            app.world().resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::Idle
        );
        assert!(app.world().get_entity(secondary).is_err());
    }

    #[test]
    fn occupied_dialog_broker_keeps_shutdown_waiting() {
        let mut app = App::new();
        app.add_message::<WindowCloseRequested>()
            .add_plugins((NativeDialogPlugin, ShutdownPlugin))
            .init_resource::<SceneIo>();
        app.world_mut().resource_mut::<SceneIo>().mark_dirty();
        let (release_sender, release_receiver) = mpsc::channel();
        app.world()
            .resource::<NativeDialogBroker>()
            .request(
                move || release_receiver.recv().expect("release sender dropped"),
                |_, _: ()| {},
            )
            .unwrap();
        assert!(request_editor_shutdown(
            app.world_mut(),
            ShutdownSource::Programmatic
        ));

        app.update();

        assert_eq!(
            app.world().resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::WaitingForBroker {
                source: ShutdownSource::Programmatic
            }
        );
        release_sender.send(()).unwrap();
    }

    #[test]
    fn successful_save_requires_every_scene_tab_to_be_clean() {
        let mut world = World::new();
        world.init_resource::<SceneIo>();
        world.insert_resource(ShutdownCoordinator {
            intent: ShutdownIntent::Saving {
                source: ShutdownSource::Programmatic,
            },
            ..default()
        });

        assert!(complete_shutdown_save(
            &mut world,
            ShutdownSaveOutcome::Saved
        ));
        assert_eq!(
            world.resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::Authorized {
                source: ShutdownSource::Programmatic,
                require_clean_scenes: true,
            }
        );

        world.resource_mut::<SceneIo>().mark_dirty();
        world.resource_mut::<ShutdownCoordinator>().intent = ShutdownIntent::Saving {
            source: ShutdownSource::Programmatic,
        };
        assert!(!complete_shutdown_save(
            &mut world,
            ShutdownSaveOutcome::Saved
        ));
        assert_eq!(
            world.resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::Idle
        );
        assert!(world
            .resource::<ShutdownCoordinator>()
            .last_blocker()
            .is_some());
    }

    #[test]
    fn a_new_edit_after_save_reopens_the_guard_instead_of_exiting() {
        let mut world = World::new();
        world.init_resource::<Messages<AppExit>>();
        world.init_resource::<SceneIo>();
        world.insert_resource(ShutdownCoordinator {
            intent: ShutdownIntent::Authorized {
                source: ShutdownSource::Programmatic,
                require_clean_scenes: true,
            },
            ..default()
        });
        world.resource_mut::<SceneIo>().mark_dirty();

        finalize_authorized_shutdown(&mut world);

        assert_eq!(
            world.resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::WaitingForBroker {
                source: ShutdownSource::Programmatic
            }
        );
        let messages = world.resource::<Messages<AppExit>>();
        assert_eq!(
            MessageCursor::<AppExit>::default().read(messages).count(),
            0
        );
    }

    #[test]
    fn last_schedule_edit_is_seen_before_final_exit_authorization() {
        let mut app = App::new();
        app.add_message::<WindowCloseRequested>()
            .add_plugins((NativeDialogPlugin, ShutdownPlugin))
            .init_resource::<SceneIo>()
            .add_systems(Last, |mut scene_io: ResMut<SceneIo>| {
                scene_io.mark_dirty();
            });
        assert!(request_editor_shutdown(
            app.world_mut(),
            ShutdownSource::Programmatic
        ));

        app.update();

        assert_eq!(app.should_exit(), None);
        assert!(app.world().resource::<SceneIo>().has_unsaved_tabs());
        assert_eq!(
            app.world().resource::<ShutdownCoordinator>().intent(),
            ShutdownIntent::WaitingForBroker {
                source: ShutdownSource::Programmatic
            }
        );
    }

    #[test]
    fn custom_dialog_labels_map_to_shutdown_decisions() {
        assert_eq!(
            decision_from_dialog(rfd::MessageDialogResult::Custom(SAVE_ALL_LABEL.into())),
            ShutdownDecision::SaveAll
        );
        assert_eq!(
            decision_from_dialog(rfd::MessageDialogResult::Custom(DISCARD_LABEL.into())),
            ShutdownDecision::Discard
        );
        assert_eq!(
            decision_from_dialog(rfd::MessageDialogResult::Custom(CANCEL_LABEL.into())),
            ShutdownDecision::Cancel
        );
    }

    #[test]
    fn only_the_final_coordinator_state_authorizes_clean_session_persistence() {
        let mut coordinator = ShutdownCoordinator::default();
        assert!(!coordinator.authorized_exit_sent());

        coordinator.intent = ShutdownIntent::Authorized {
            source: ShutdownSource::Programmatic,
            require_clean_scenes: true,
        };
        assert!(!coordinator.authorized_exit_sent());

        coordinator.intent = ShutdownIntent::ExitSent {
            source: ShutdownSource::Programmatic,
        };
        assert!(coordinator.authorized_exit_sent());
    }
}
