//! macOS platform backend entrypoints.
//!
//! Native backend implemented on top of Cocoa/AppKit runtime messaging.

#![cfg(target_os = "macos")]

use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::ffi::{CStr, CString, c_void};
use std::ptr::NonNull;
use std::rc::Rc;
use std::sync::{Arc, Mutex, MutexGuard, Once, OnceLock};

use objc2::rc::Retained;
use objc2::runtime::{AnyClass, AnyObject, Bool, ClassBuilder, Sel};
use objc2::sel;
use objc2_core_foundation::{
    CFRetained, CFRunLoop, CFRunLoopSource, CFRunLoopSourceContext, CGPoint, CGRect, CGSize,
    kCFRunLoopCommonModes,
};
use objc2_foundation::{NSString, ns_string};
use objc2_quartz_core::{CAMetalLayer, kCAGravityTopLeft};
use raw_window_handle::{
    AppKitDisplayHandle, AppKitWindowHandle, DisplayHandle, HandleError, HasDisplayHandle,
    HasWindowHandle, RawDisplayHandle, RawWindowHandle, WindowHandle,
};
use raw_window_metal::Layer as MetalLayer;
use ruin_ui::{
    CursorIcon, KeyboardEvent, KeyboardEventKind, KeyboardKey, KeyboardModifiers, PlatformEndpoint,
    PlatformEvent, PlatformRequest, PlatformRuntime, Point, PointerButton, PointerEvent,
    PointerEventKind, SceneSnapshot, UiRuntime, UiSize, WindowConfigured, WindowId,
    WindowLifecycle, WindowSpec, WindowUpdate, set_command_wake_hook,
};
use ruin_ui_renderer_wgpu::{RenderError, WgpuSceneRenderer};
use tracing::{debug, trace};

#[link(name = "AppKit", kind = "framework")]
unsafe extern "C" {}

const NS_APPLICATION_ACTIVATION_POLICY_REGULAR: isize = 0;
const NS_BACKING_STORE_BUFFERED: usize = 2;
const NS_WINDOW_STYLE_TITLED: usize = 1 << 0;
const NS_WINDOW_STYLE_CLOSABLE: usize = 1 << 1;
const NS_WINDOW_STYLE_MINIATURIZABLE: usize = 1 << 2;
const NS_WINDOW_STYLE_RESIZABLE: usize = 1 << 3;
const NS_VIEW_WIDTH_SIZABLE: usize = 1 << 1;
const NS_VIEW_HEIGHT_SIZABLE: usize = 1 << 4;
const NS_VIEW_LAYER_CONTENTS_REDRAW_DURING_VIEW_RESIZE: usize = 2;
const NS_VIEW_LAYER_CONTENTS_PLACEMENT_TOP_LEFT: usize = 11;

const NSEVENT_MODIFIER_FLAG_SHIFT: usize = 1 << 17;
const NSEVENT_MODIFIER_FLAG_CONTROL: usize = 1 << 18;
const NSEVENT_MODIFIER_FLAG_OPTION: usize = 1 << 19;
const NSEVENT_MODIFIER_FLAG_COMMAND: usize = 1 << 20;

struct MacosState {
    app: *mut AnyObject,
    commands: ruin_runtime::channel::mpsc::Receiver<PlatformRequest>,
    endpoint_events: ruin_runtime::channel::mpsc::UnboundedSender<PlatformEvent>,
    _run_loop_source: RunLoopSourceRegistration,
    shutdown_requested: bool,
    windows: BTreeMap<WindowId, MacosWindow>,
}

struct RunLoopSourceRegistration {
    _run_loop: CFRetained<CFRunLoop>,
    _source: CFRetained<CFRunLoopSource>,
}

#[derive(Clone)]
struct RunLoopWakeHandle {
    run_loop: CFRetained<CFRunLoop>,
    source: CFRetained<CFRunLoopSource>,
}

// CFRunLoopSourceSignal and CFRunLoopWakeUp are designed for cross-thread wakeups.
// The handle owns retained references and never exposes mutable access to the CF objects.
unsafe impl Send for RunLoopWakeHandle {}
unsafe impl Sync for RunLoopWakeHandle {}

struct MacosWindow {
    spec: WindowSpec,
    state: MacosWindowState,
    configuration: WindowConfigured,
    latest_scene: Option<SceneSnapshot>,
    clipboard_text: Option<String>,
    cursor_icon: CursorIcon,
    resize_pipeline: ResizePipeline,
    native: Option<NativeWindow>,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct MacosWindowState {
    lifecycle: WindowLifecycle,
    desired_visible: bool,
    close_requested_sent: bool,
    close_in_progress: bool,
    has_presented_frame: bool,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PresentationFlushPolicy {
    None,
    DisplayIfNeeded,
    DisplayAndTransaction,
}

impl MacosWindowState {
    fn new(desired_visible: bool) -> Self {
        Self {
            lifecycle: WindowLifecycle::LogicalClosed,
            desired_visible,
            close_requested_sent: false,
            close_in_progress: false,
            has_presented_frame: false,
        }
    }

    fn open_with_visibility(&mut self, visible: bool) {
        self.lifecycle = lifecycle_for_visibility(visible);
        self.desired_visible = visible;
        self.close_requested_sent = false;
        self.close_in_progress = false;
    }

    fn set_native_visibility(&mut self, visible: bool) {
        self.lifecycle = lifecycle_for_visibility(visible);
        if visible {
            self.close_requested_sent = false;
        } else {
            self.has_presented_frame = false;
        }
    }

    fn request_logical_close(&mut self) {
        self.desired_visible = false;
        self.close_in_progress = true;
    }

    fn finalize_close(&mut self) {
        self.lifecycle = WindowLifecycle::LogicalClosed;
        self.desired_visible = false;
        self.close_in_progress = false;
        self.has_presented_frame = false;
    }

    fn note_presented(&mut self) {
        self.has_presented_frame = true;
    }

    fn mark_close_requested_sent(&mut self) -> bool {
        if self.close_requested_sent {
            false
        } else {
            self.close_requested_sent = true;
            true
        }
    }

    fn note_delegate_close_request(&mut self) -> bool {
        !self.close_in_progress && self.mark_close_requested_sent()
    }
}

fn lifecycle_for_visibility(visible: bool) -> WindowLifecycle {
    if visible {
        WindowLifecycle::OpenVisible
    } else {
        WindowLifecycle::OpenHidden
    }
}

struct ObjcStrong {
    object: Option<NonNull<AnyObject>>,
}

impl ObjcStrong {
    unsafe fn from_owned(object: *mut AnyObject) -> Option<Self> {
        Some(Self {
            object: Some(NonNull::new(object)?),
        })
    }

    fn as_ptr(&self) -> *mut AnyObject {
        self.object
            .map_or(std::ptr::null_mut(), |object| object.as_ptr())
    }

    fn release_now(&mut self) {
        let Some(object) = self.object.take() else {
            return;
        };
        unsafe {
            let _: () = objc2::msg_send![object.as_ptr(), release];
        }
    }
}

impl Drop for ObjcStrong {
    fn drop(&mut self) {
        self.release_now();
    }
}

struct NativeWindow {
    // Renderer/surface must drop before the AppKit objects whose raw handles it
    // was created from.
    renderer: Option<WgpuSceneRenderer>,
    _metal_layer: Retained<CAMetalLayer>,
    view: ObjcStrong,
    delegate: Option<ObjcStrong>,
    window: ObjcStrong,
}

impl NativeWindow {
    fn window_ptr(&self) -> usize {
        self.window.as_ptr() as usize
    }

    fn window_object(&self) -> *mut AnyObject {
        self.window.as_ptr()
    }

    fn view_object(&self) -> *mut AnyObject {
        self.view.as_ptr()
    }

    fn set_title(&self, title: &str) {
        let title = NSString::from_str(title);
        unsafe {
            let _: () = objc2::msg_send![self.window_object(), setTitle: &*title];
        }
    }

    fn set_content_size(&self, size: UiSize) {
        let content_size = CGSize::new(size.width as f64, size.height as f64);
        unsafe {
            let _: () = objc2::msg_send![self.window_object(), setContentSize: content_size];
        }
    }

    fn show(&self) {
        unsafe {
            let _: () = objc2::msg_send![self.window_object(), makeKeyAndOrderFront: std::ptr::null_mut::<AnyObject>()];
        }
    }

    fn hide(&self) {
        unsafe {
            let _: () =
                objc2::msg_send![self.window_object(), orderOut: std::ptr::null_mut::<AnyObject>()];
        }
    }

    fn close(&self) {
        unsafe {
            let _: () = objc2::msg_send![self.window_object(), close];
        }
    }

    fn in_live_resize(&self) -> bool {
        unsafe { objc2::msg_send![self.view_object(), inLiveResize] }
    }

    fn display_if_needed(&self) {
        unsafe {
            let _: () = objc2::msg_send![self.view_object(), displayIfNeeded];
            let _: () = objc2::msg_send![self.window_object(), displayIfNeeded];
        }
    }

    fn flush_core_animation_transaction(&self) {
        unsafe {
            let _: () = objc2::msg_send![objc2::class!(CATransaction), flush];
        }
    }

    fn detach_delegate(&mut self) {
        unsafe {
            let _: () = objc2::msg_send![self.window_object(), setDelegate: std::ptr::null_mut::<AnyObject>()];
        }
        if let Some(mut delegate) = self.delegate.take() {
            delegate.release_now();
        }
    }
}

impl Drop for NativeWindow {
    fn drop(&mut self) {
        self.detach_delegate();
    }
}

#[derive(Default)]
struct DelegateState {
    pending_close_requests: Vec<usize>,
    pending_closed_windows: Vec<usize>,
    pending_resize_events: Vec<DelegateResizeEvent>,
    pending_input_events: Vec<DelegateInputEvent>,
}

#[derive(Clone, Copy)]
struct DelegateResizeEvent {
    window_ptr: usize,
    size: UiSize,
    scale_factor: f32,
}

enum DelegateInputEvent {
    Pointer {
        window_ptr: usize,
        event: PointerEvent,
    },
    Keyboard {
        window_ptr: usize,
        event: KeyboardEvent,
    },
}

thread_local! {
    static ACTIVE_MACOS_STATE: RefCell<Option<Rc<RefCell<MacosState>>>> = const { RefCell::new(None) };
    static APPKIT_PUMP_ACTIVE: Cell<bool> = const { Cell::new(false) };
}

fn run_loop_wake_handle() -> &'static Mutex<Option<RunLoopWakeHandle>> {
    static HANDLE: OnceLock<Mutex<Option<RunLoopWakeHandle>>> = OnceLock::new();
    HANDLE.get_or_init(|| Mutex::new(None))
}

#[derive(Clone, Copy, Debug, Default, PartialEq)]
struct ResizePipeline {
    active_resize: Option<WindowConfigured>,
    next_resize: Option<WindowConfigured>,
}

impl ResizePipeline {
    fn note_resize(&mut self, configuration: WindowConfigured) -> Option<WindowConfigured> {
        if self.active_resize.is_some() {
            self.next_resize = Some(configuration);
            return None;
        }
        self.active_resize = Some(configuration);
        Some(configuration)
    }

    fn ensure_initial_request(
        &mut self,
        configuration: WindowConfigured,
    ) -> Option<WindowConfigured> {
        if self.active_resize.is_some() || self.next_resize.is_some() {
            return None;
        }
        self.active_resize = Some(configuration);
        Some(configuration)
    }

    fn note_presented_scene(&mut self, scene_viewport: UiSize) -> Option<WindowConfigured> {
        let presented_configuration = self.active_resize?;
        if presented_configuration.actual_inner_size != scene_viewport {
            return None;
        }

        self.active_resize = None;
        if self.next_resize == Some(presented_configuration) {
            self.next_resize = None;
        }

        let next_resize = self.next_resize.take()?;
        self.active_resize = Some(next_resize);
        Some(next_resize)
    }
}

impl MacosWindow {
    fn new(spec: WindowSpec) -> Self {
        let desired_visible = spec.visible;
        let actual_inner_size = spec
            .requested_inner_size
            .unwrap_or_else(|| UiSize::new(960.0, 640.0));
        Self {
            spec,
            state: MacosWindowState::new(desired_visible),
            configuration: WindowConfigured {
                actual_inner_size,
                scale_factor: 1.0,
                visible: false,
                maximized: false,
                fullscreen: false,
            },
            latest_scene: None,
            clipboard_text: None,
            cursor_icon: CursorIcon::Default,
            resize_pipeline: ResizePipeline::default(),
            native: None,
        }
    }
}

#[derive(Clone)]
struct AppKitSurfaceTarget {
    view: *mut AnyObject,
}

// NSView pointer is only used on the owning thread by the renderer; this is
// required by wgpu surface creation bounds.
unsafe impl Send for AppKitSurfaceTarget {}
unsafe impl Sync for AppKitSurfaceTarget {}

impl HasDisplayHandle for AppKitSurfaceTarget {
    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
        assert_main_thread();
        let raw = RawDisplayHandle::AppKit(AppKitDisplayHandle::new());
        Ok(unsafe { DisplayHandle::borrow_raw(raw) })
    }
}

impl HasWindowHandle for AppKitSurfaceTarget {
    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
        assert_main_thread();
        let ptr = NonNull::new(self.view.cast::<c_void>()).ok_or(HandleError::Unavailable)?;
        let raw = RawWindowHandle::AppKit(AppKitWindowHandle::new(ptr));
        Ok(unsafe { WindowHandle::borrow_raw(raw) })
    }
}

/// Starts the macOS platform runtime.
///
/// Must be called on the process main thread.
pub fn start_macos_platform() -> PlatformRuntime {
    assert_main_thread();
    let app = ensure_ns_application();
    let run_loop_source =
        install_platform_run_loop_source().expect("macOS run-loop source must install");

    PlatformRuntime::custom_local(|endpoint: PlatformEndpoint| {
        let state = Rc::new(RefCell::new(MacosState {
            app,
            commands: endpoint.commands,
            endpoint_events: endpoint.events.clone(),
            _run_loop_source: run_loop_source,
            shutdown_requested: false,
            windows: BTreeMap::new(),
        }));
        ACTIVE_MACOS_STATE.with(|slot| {
            *slot.borrow_mut() = Some(Rc::clone(&state));
        });
    })
}

/// Starts a UI runtime backed by the macOS platform runtime.
pub fn start_macos_ui() -> UiRuntime {
    UiRuntime::from_platform(start_macos_platform())
}

pub fn run_macos_app_event_loop() {
    assert_main_thread();
    let app = ensure_ns_application();
    unsafe {
        let _: () = objc2::msg_send![app, run];
    }
    set_command_wake_hook(None);
    if let Ok(mut slot) = run_loop_wake_handle().lock() {
        *slot = None;
    }
}

fn install_platform_run_loop_source() -> Option<RunLoopSourceRegistration> {
    let run_loop = CFRunLoop::main()?;
    let mut context = CFRunLoopSourceContext {
        version: 0,
        info: std::ptr::null_mut(),
        retain: None,
        release: None,
        copyDescription: None,
        equal: None,
        hash: None,
        schedule: None,
        cancel: None,
        perform: Some(platform_run_loop_source_perform),
    };
    let source = unsafe { CFRunLoopSource::new(None, 0, &mut context) }?;
    let common_modes = unsafe { kCFRunLoopCommonModes };
    run_loop.add_source(Some(&source), common_modes);

    let handle = RunLoopWakeHandle {
        run_loop: run_loop.clone(),
        source: source.clone(),
    };
    if let Ok(mut slot) = run_loop_wake_handle().lock() {
        *slot = Some(handle);
    }
    set_command_wake_hook(Some(Arc::new(signal_platform_run_loop_source)));

    Some(RunLoopSourceRegistration {
        _run_loop: run_loop,
        _source: source,
    })
}

extern "C-unwind" fn platform_run_loop_source_perform(_info: *mut c_void) {
    let Some(state) = ACTIVE_MACOS_STATE.with(|slot| slot.borrow().as_ref().cloned()) else {
        return;
    };
    drain_macos_run_loop_work(&state);
}

fn signal_platform_run_loop_source() {
    let Some(handle) = run_loop_wake_handle()
        .lock()
        .ok()
        .and_then(|slot| slot.clone())
    else {
        return;
    };
    handle.source.signal();
    handle.run_loop.wake_up();
}

fn assert_main_thread() {
    let is_main = unsafe { libc::pthread_main_np() == 1 };
    assert!(
        is_main,
        "ruin_ui_platform_macos must be started on the process main thread"
    );
}

fn ensure_ns_application() -> *mut AnyObject {
    let app: *mut AnyObject =
        unsafe { objc2::msg_send![objc2::class!(NSApplication), sharedApplication] };
    install_app_delegate(app);
    unsafe {
        let _: bool =
            objc2::msg_send![app, setActivationPolicy: NS_APPLICATION_ACTIVATION_POLICY_REGULAR];
        let _: () = objc2::msg_send![app, finishLaunching];
        let _: () = objc2::msg_send![app, activateIgnoringOtherApps: true];
    }
    app
}

fn app_delegate_class() -> &'static AnyClass {
    static REGISTER: Once = Once::new();
    static CLASS: OnceLock<&'static AnyClass> = OnceLock::new();

    REGISTER.call_once(|| {
        extern "C-unwind" fn terminate_after_last_window_closed(
            _this: &AnyObject,
            _cmd: Sel,
            _sender: *mut AnyObject,
        ) -> Bool {
            Bool::YES
        }

        let class_name = CString::new("RuinAppDelegate").expect("class name is valid");
        if let Some(mut builder) = ClassBuilder::new(&class_name, objc2::class!(NSObject)) {
            unsafe {
                let method: extern "C-unwind" fn(_, _, _) -> _ = terminate_after_last_window_closed;
                builder.add_method::<AnyObject, _>(
                    sel!(applicationShouldTerminateAfterLastWindowClosed:),
                    method,
                );
            }
            let _ = CLASS.set(builder.register());
        } else {
            let existing =
                AnyClass::get(class_name.as_c_str()).expect("RuinAppDelegate class must exist");
            let _ = CLASS.set(existing);
        }
    });

    CLASS
        .get()
        .expect("RuinAppDelegate class registration must succeed")
}

fn install_app_delegate(app: *mut AnyObject) {
    static INSTANCE: OnceLock<usize> = OnceLock::new();
    let delegate = *INSTANCE.get_or_init(|| {
        let class = app_delegate_class();
        let delegate_obj: *mut AnyObject = unsafe { objc2::msg_send![class, new] };
        delegate_obj as usize
    }) as *mut AnyObject;
    if delegate.is_null() {
        return;
    }
    unsafe {
        let _: () = objc2::msg_send![app, setDelegate: delegate];
    }
}

fn delegate_state() -> &'static Mutex<DelegateState> {
    static DELEGATE_STATE: OnceLock<Mutex<DelegateState>> = OnceLock::new();
    DELEGATE_STATE.get_or_init(|| Mutex::new(DelegateState::default()))
}

fn lock_delegate_state() -> MutexGuard<'static, DelegateState> {
    delegate_state()
        .lock()
        .expect("macOS delegate event state mutex poisoned")
}

fn queue_delegate_close_request(window: *mut AnyObject) {
    if window.is_null() {
        return;
    }
    lock_delegate_state()
        .pending_close_requests
        .push(window as usize);
}

fn queue_delegate_window_closed(window: *mut AnyObject) {
    if window.is_null() {
        return;
    }
    lock_delegate_state()
        .pending_closed_windows
        .push(window as usize);
}

fn queue_delegate_resize(window: *mut AnyObject, size: UiSize, scale_factor: f32) {
    if window.is_null() {
        return;
    }
    lock_delegate_state()
        .pending_resize_events
        .push(DelegateResizeEvent {
            window_ptr: window as usize,
            size,
            scale_factor,
        });
}

fn queue_delegate_configuration_from_window(window: *mut AnyObject) {
    if window.is_null() {
        return;
    }
    let view: *mut AnyObject = unsafe { objc2::msg_send![window, contentView] };
    if view.is_null() {
        return;
    }
    let frame: CGRect = unsafe { objc2::msg_send![view, frame] };
    let scale_factor: f64 = unsafe { objc2::msg_send![window, backingScaleFactor] };
    queue_delegate_resize(
        window,
        UiSize::new(frame.size.width as f32, frame.size.height as f32),
        (scale_factor as f32).max(1.0),
    );
}

fn queue_delegate_pointer_event(window: *mut AnyObject, event: PointerEvent) {
    if window.is_null() {
        return;
    }
    lock_delegate_state()
        .pending_input_events
        .push(DelegateInputEvent::Pointer {
            window_ptr: window as usize,
            event,
        });
}

fn queue_delegate_keyboard_event(window: *mut AnyObject, event: KeyboardEvent) {
    if window.is_null() {
        return;
    }
    lock_delegate_state()
        .pending_input_events
        .push(DelegateInputEvent::Keyboard {
            window_ptr: window as usize,
            event,
        });
}

fn take_delegate_close_requests() -> Vec<usize> {
    std::mem::take(&mut lock_delegate_state().pending_close_requests)
}

fn take_delegate_closed_windows() -> Vec<usize> {
    std::mem::take(&mut lock_delegate_state().pending_closed_windows)
}

fn take_delegate_resize_events() -> Vec<DelegateResizeEvent> {
    std::mem::take(&mut lock_delegate_state().pending_resize_events)
}

fn take_delegate_input_events() -> Vec<DelegateInputEvent> {
    std::mem::take(&mut lock_delegate_state().pending_input_events)
}

fn window_delegate_class() -> &'static AnyClass {
    static REGISTER: Once = Once::new();
    static CLASS: OnceLock<&'static AnyClass> = OnceLock::new();

    REGISTER.call_once(|| {
        extern "C-unwind" fn should_close(
            _this: &AnyObject,
            _cmd: Sel,
            sender: *mut AnyObject,
        ) -> Bool {
            debug!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = sender as usize,
                "windowShouldClose:"
            );
            queue_delegate_close_request(sender);
            signal_platform_run_loop_source();
            Bool::YES
        }
        extern "C-unwind" fn did_close(_this: &AnyObject, _cmd: Sel, notification: *mut AnyObject) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            debug!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = window as usize,
                "windowWillClose:"
            );
            queue_delegate_window_closed(window);
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_resize(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::resize",
                window_ptr = window as usize,
                "windowDidResize:"
            );
            queue_delegate_configuration_from_window(window);
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_change_screen(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::resize",
                window_ptr = window as usize,
                "windowDidChangeScreen:"
            );
            queue_delegate_configuration_from_window(window);
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_change_occlusion_state(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = window as usize,
                "windowDidChangeOcclusionState:"
            );
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_become_key(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = window as usize,
                "windowDidBecomeKey:"
            );
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_miniaturize(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = window as usize,
                "windowDidMiniaturize:"
            );
            signal_platform_run_loop_source();
        }
        extern "C-unwind" fn did_deminiaturize(
            _this: &AnyObject,
            _cmd: Sel,
            notification: *mut AnyObject,
        ) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![notification, object] };
            trace!(
                target: "ruin_ui_platform_macos::lifecycle",
                window_ptr = window as usize,
                "windowDidDeminiaturize:"
            );
            signal_platform_run_loop_source();
        }

        let class_name = CString::new("RuinWindowDelegate").expect("class name is valid");
        if let Some(mut builder) = ClassBuilder::new(&class_name, objc2::class!(NSObject)) {
            unsafe {
                let method: extern "C-unwind" fn(_, _, _) -> _ = should_close;
                builder.add_method::<AnyObject, _>(sel!(windowShouldClose:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_close;
                builder.add_method::<AnyObject, _>(sel!(windowWillClose:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_resize;
                builder.add_method::<AnyObject, _>(sel!(windowDidResize:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_change_screen;
                builder.add_method::<AnyObject, _>(sel!(windowDidChangeScreen:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_change_occlusion_state;
                builder.add_method::<AnyObject, _>(sel!(windowDidChangeOcclusionState:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_become_key;
                builder.add_method::<AnyObject, _>(sel!(windowDidBecomeKey:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_miniaturize;
                builder.add_method::<AnyObject, _>(sel!(windowDidMiniaturize:), method);
                let method: extern "C-unwind" fn(_, _, _) = did_deminiaturize;
                builder.add_method::<AnyObject, _>(sel!(windowDidDeminiaturize:), method);
            }
            let _ = CLASS.set(builder.register());
        } else {
            let existing = AnyClass::get(class_name.as_c_str())
                .expect("RuinWindowDelegate class must be available");
            let _ = CLASS.set(existing);
        }
    });

    CLASS
        .get()
        .expect("RuinWindowDelegate class registration must succeed")
}

fn content_view_class() -> &'static AnyClass {
    static REGISTER: Once = Once::new();
    static CLASS: OnceLock<&'static AnyClass> = OnceLock::new();

    REGISTER.call_once(|| {
        extern "C-unwind" fn accepts_first_responder(_this: &AnyObject, _cmd: Sel) -> Bool {
            Bool::YES
        }

        extern "C-unwind" fn is_flipped(_this: &AnyObject, _cmd: Sel) -> Bool {
            Bool::YES
        }

        extern "C-unwind" fn did_change_backing_properties(this: &AnyObject, _cmd: Sel) {
            let window: *mut AnyObject = unsafe { objc2::msg_send![this, window] };
            trace!(
                target: "ruin_ui_platform_macos::resize",
                window_ptr = window as usize,
                "viewDidChangeBackingProperties"
            );
            queue_delegate_configuration_from_window(window);
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn mouse_moved(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            queue_pointer_from_view_event(this, event, PointerEventKind::Move);
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn left_mouse_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            queue_pointer_from_view_event(
                this,
                event,
                PointerEventKind::Down {
                    button: PointerButton::Primary,
                },
            );
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn left_mouse_up(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            queue_pointer_from_view_event(
                this,
                event,
                PointerEventKind::Up {
                    button: PointerButton::Primary,
                },
            );
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn left_mouse_dragged(
            this: &AnyObject,
            _cmd: Sel,
            event: *mut AnyObject,
        ) {
            queue_pointer_from_view_event(this, event, PointerEventKind::Move);
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn other_mouse_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            let button_number: usize = unsafe { objc2::msg_send![event, buttonNumber] };
            if button_number == 2 {
                queue_pointer_from_view_event(
                    this,
                    event,
                    PointerEventKind::Down {
                        button: PointerButton::Middle,
                    },
                );
                signal_platform_run_loop_source();
            }
        }

        extern "C-unwind" fn other_mouse_up(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            let button_number: usize = unsafe { objc2::msg_send![event, buttonNumber] };
            if button_number == 2 {
                queue_pointer_from_view_event(
                    this,
                    event,
                    PointerEventKind::Up {
                        button: PointerButton::Middle,
                    },
                );
                signal_platform_run_loop_source();
            }
        }

        extern "C-unwind" fn other_mouse_dragged(
            this: &AnyObject,
            _cmd: Sel,
            event: *mut AnyObject,
        ) {
            let button_number: usize = unsafe { objc2::msg_send![event, buttonNumber] };
            if button_number == 2 {
                queue_pointer_from_view_event(this, event, PointerEventKind::Move);
                signal_platform_run_loop_source();
            }
        }

        extern "C-unwind" fn scroll_wheel(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            let delta_x: f64 = unsafe { objc2::msg_send![event, scrollingDeltaX] };
            let delta_y: f64 = unsafe { objc2::msg_send![event, scrollingDeltaY] };
            queue_pointer_from_view_event(
                this,
                event,
                PointerEventKind::Scroll {
                    delta: Point::new(delta_x as f32, -(delta_y as f32)),
                },
            );
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn key_down(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            queue_keyboard_from_view_event(this, event, KeyboardEventKind::Pressed);
            signal_platform_run_loop_source();
        }

        extern "C-unwind" fn key_up(this: &AnyObject, _cmd: Sel, event: *mut AnyObject) {
            queue_keyboard_from_view_event(this, event, KeyboardEventKind::Released);
            signal_platform_run_loop_source();
        }

        let class_name = CString::new("RuinContentView").expect("class name is valid");
        if let Some(mut builder) = ClassBuilder::new(&class_name, objc2::class!(NSView)) {
            unsafe {
                let method: extern "C-unwind" fn(_, _) -> _ = accepts_first_responder;
                builder.add_method::<AnyObject, _>(sel!(acceptsFirstResponder), method);
                let method: extern "C-unwind" fn(_, _) -> _ = is_flipped;
                builder.add_method::<AnyObject, _>(sel!(isFlipped), method);
                let method: extern "C-unwind" fn(_, _) = did_change_backing_properties;
                builder.add_method::<AnyObject, _>(sel!(viewDidChangeBackingProperties), method);
                let method: extern "C-unwind" fn(_, _, _) = mouse_moved;
                builder.add_method::<AnyObject, _>(sel!(mouseMoved:), method);
                let method: extern "C-unwind" fn(_, _, _) = mouse_moved;
                builder.add_method::<AnyObject, _>(sel!(rightMouseDragged:), method);
                let method: extern "C-unwind" fn(_, _, _) = left_mouse_down;
                builder.add_method::<AnyObject, _>(sel!(mouseDown:), method);
                let method: extern "C-unwind" fn(_, _, _) = left_mouse_up;
                builder.add_method::<AnyObject, _>(sel!(mouseUp:), method);
                let method: extern "C-unwind" fn(_, _, _) = left_mouse_dragged;
                builder.add_method::<AnyObject, _>(sel!(mouseDragged:), method);
                let method: extern "C-unwind" fn(_, _, _) = other_mouse_down;
                builder.add_method::<AnyObject, _>(sel!(otherMouseDown:), method);
                let method: extern "C-unwind" fn(_, _, _) = other_mouse_up;
                builder.add_method::<AnyObject, _>(sel!(otherMouseUp:), method);
                let method: extern "C-unwind" fn(_, _, _) = other_mouse_dragged;
                builder.add_method::<AnyObject, _>(sel!(otherMouseDragged:), method);
                let method: extern "C-unwind" fn(_, _, _) = scroll_wheel;
                builder.add_method::<AnyObject, _>(sel!(scrollWheel:), method);
                let method: extern "C-unwind" fn(_, _, _) = key_down;
                builder.add_method::<AnyObject, _>(sel!(keyDown:), method);
                let method: extern "C-unwind" fn(_, _, _) = key_up;
                builder.add_method::<AnyObject, _>(sel!(keyUp:), method);
            }
            let _ = CLASS.set(builder.register());
        } else {
            let existing =
                AnyClass::get(class_name.as_c_str()).expect("RuinContentView class must exist");
            let _ = CLASS.set(existing);
        }
    });

    CLASS
        .get()
        .expect("RuinContentView class registration must succeed")
}

fn event_window_for_view(this: &AnyObject, event: *mut AnyObject) -> *mut AnyObject {
    let mut window: *mut AnyObject = unsafe { objc2::msg_send![event, window] };
    if window.is_null() {
        window = unsafe { objc2::msg_send![this, window] };
    }
    window
}

fn pointer_position_in_view(this: &AnyObject, event: *mut AnyObject) -> Point {
    let location: CGPoint = unsafe { objc2::msg_send![event, locationInWindow] };
    let frame: CGRect = unsafe { objc2::msg_send![this, frame] };
    Point::new(
        location.x as f32,
        frame.size.height as f32 - location.y as f32,
    )
}

fn queue_pointer_from_view_event(this: &AnyObject, event: *mut AnyObject, kind: PointerEventKind) {
    let window = event_window_for_view(this, event);
    let position = pointer_position_in_view(this, event);
    queue_delegate_pointer_event(window, PointerEvent::new(0, position, kind));
}

fn queue_keyboard_from_view_event(
    this: &AnyObject,
    event: *mut AnyObject,
    kind: KeyboardEventKind,
) {
    let window = event_window_for_view(this, event);
    let keycode: u16 = unsafe { objc2::msg_send![event, keyCode] };
    let text = if kind == KeyboardEventKind::Pressed {
        event_characters(event)
    } else {
        None
    };
    let key = keyboard_key(keycode, text.as_deref());
    queue_delegate_keyboard_event(
        window,
        KeyboardEvent::new(
            u32::from(keycode),
            kind,
            key,
            keyboard_modifiers(event),
            text,
        ),
    );
}

fn flush_pending_events(
    sender: &ruin_runtime::channel::mpsc::UnboundedSender<PlatformEvent>,
    pending: &mut Vec<PlatformEvent>,
) {
    for event in pending.drain(..) {
        let _ = sender.send(event);
    }
}

struct AppKitPumpGuard;

impl Drop for AppKitPumpGuard {
    fn drop(&mut self) {
        APPKIT_PUMP_ACTIVE.with(|flag| flag.set(false));
    }
}

fn try_enter_appkit_pump() -> Option<AppKitPumpGuard> {
    APPKIT_PUMP_ACTIVE.with(|flag| {
        if flag.replace(true) {
            None
        } else {
            Some(AppKitPumpGuard)
        }
    })
}

fn drain_macos_run_loop_work(state: &Rc<RefCell<MacosState>>) {
    let Some(_guard) = try_enter_appkit_pump() else {
        return;
    };
    let (shutdown_requested, sender) = {
        let Ok(state_ref) = state.try_borrow() else {
            return;
        };
        (
            state_ref.shutdown_requested,
            state_ref.endpoint_events.clone(),
        )
    };
    if shutdown_requested {
        return;
    }
    let mut pending = Vec::new();

    let _ = drain_platform_commands(state);

    {
        let Ok(mut state_ref) = state.try_borrow_mut() else {
            return;
        };
        unsafe {
            let _: () = objc2::msg_send![state_ref.app, updateWindows];
        }
        for (window_id, window) in &mut state_ref.windows {
            collect_window_state_events(*window_id, window, &mut pending);
        }
        apply_delegate_events(&mut state_ref, &mut pending);
    }

    flush_pending_events(&sender, &mut pending);
}

fn take_next_platform_command(state: &Rc<RefCell<MacosState>>) -> Option<PlatformRequest> {
    let mut state_ref = state.borrow_mut();
    if state_ref.shutdown_requested {
        return None;
    }
    state_ref.commands.try_recv().ok()
}

fn drain_platform_commands(state: &Rc<RefCell<MacosState>>) -> bool {
    let mut handled_any = false;
    while let Some(command) = take_next_platform_command(state) {
        handled_any = true;
        match command {
            PlatformRequest::CreateWindow { window_id, spec } => {
                handle_create_window(state, window_id, spec);
            }
            PlatformRequest::UpdateWindow { window_id, update } => {
                handle_update_window(state, window_id, update);
            }
            PlatformRequest::ReplaceScene { window_id, scene } => {
                handle_replace_scene(state, window_id, scene);
            }
            PlatformRequest::SetClipboardText { window_id, text } => {
                if let Some(window) = state.borrow_mut().windows.get_mut(&window_id) {
                    window.clipboard_text = Some(text);
                }
            }
            PlatformRequest::RequestClipboardText { window_id } => {
                let text = state
                    .borrow()
                    .windows
                    .get(&window_id)
                    .and_then(|window| window.clipboard_text.clone())
                    .unwrap_or_default();
                emit(state, PlatformEvent::ClipboardText { window_id, text });
            }
            PlatformRequest::SetCursorIcon { window_id, cursor } => {
                if let Some(window) = state.borrow_mut().windows.get_mut(&window_id) {
                    window.cursor_icon = cursor;
                    apply_cursor_icon(cursor);
                }
            }
            PlatformRequest::EmitCloseRequested { window_id } => {
                emit(state, PlatformEvent::CloseRequested { window_id });
            }
            PlatformRequest::EmitPointerEvent { window_id, event } => {
                emit(state, PlatformEvent::Pointer { window_id, event });
            }
            PlatformRequest::EmitKeyboardEvent { window_id, event } => {
                emit(state, PlatformEvent::Keyboard { window_id, event });
            }
            PlatformRequest::EmitWake { window_id, token } => {
                emit(state, PlatformEvent::Wake { window_id, token });
            }
            #[cfg(target_os = "linux")]
            PlatformRequest::SetPrimarySelectionText { .. } => {}
            #[cfg(target_os = "linux")]
            PlatformRequest::RequestPrimarySelectionText { .. } => {}
            PlatformRequest::Shutdown => {
                let app = {
                    let mut state_ref = state.borrow_mut();
                    state_ref.shutdown_requested = true;
                    state_ref.app
                };
                unsafe {
                    let _: () = objc2::msg_send![app, stop: std::ptr::null_mut::<AnyObject>()];
                }
                set_command_wake_hook(None);
                break;
            }
        }
    }
    handled_any
}

fn handle_create_window(state: &Rc<RefCell<MacosState>>, window_id: WindowId, spec: WindowSpec) {
    let open = spec.open;
    let visible = spec.visible;
    let mut emit_opened = false;
    let mut emit_configured = None;
    let mut emit_visibility = None;

    {
        let mut state_ref = state.borrow_mut();
        state_ref.windows.insert(window_id, MacosWindow::new(spec));
        if open {
            let app = state_ref.app;
            if let Some(window) = state_ref.windows.get_mut(&window_id) {
                ensure_native_window(window, app);
                sync_window_metrics(window);
                window.state.open_with_visibility(visible);
                window.configuration.visible = visible;
                let _ = window
                    .resize_pipeline
                    .ensure_initial_request(window.configuration);
                sync_visibility(window);
                apply_cursor_icon(window.cursor_icon);
                emit_opened = true;
                emit_configured = Some(window.configuration);
                emit_visibility = Some(visible);
            }
        }
    }

    if emit_opened {
        emit(state, PlatformEvent::Opened { window_id });
    }
    if let Some(configuration) = emit_configured {
        emit(
            state,
            PlatformEvent::Configured {
                window_id,
                configuration,
            },
        );
    }
    if let Some(visible) = emit_visibility {
        emit(
            state,
            PlatformEvent::VisibilityChanged { window_id, visible },
        );
    }
}

fn handle_update_window(
    state: &Rc<RefCell<MacosState>>,
    window_id: WindowId,
    update: WindowUpdate,
) {
    let mut emit_opened = false;
    let mut emit_visibility = None;
    let mut emit_configured = None;
    let mut emit_closed = false;

    {
        let mut state_ref = state.borrow_mut();
        let app = state_ref.app;
        let Some(window) = state_ref.windows.get_mut(&window_id) else {
            return;
        };

        if let Some(title) = update.title {
            window.spec.title = title;
            if let Some(native) = window.native.as_ref() {
                native.set_title(&window.spec.title);
            }
        }

        if let Some(open) = update.open {
            if open {
                if window.state.lifecycle == WindowLifecycle::LogicalClosed {
                    ensure_native_window(window, app);
                    sync_window_metrics(window);
                    window
                        .state
                        .open_with_visibility(window.configuration.visible);
                    sync_visibility(window);
                    apply_cursor_icon(window.cursor_icon);
                    emit_opened = true;
                }
            } else if window.state.lifecycle != WindowLifecycle::LogicalClosed {
                debug!(
                    target: "ruin_ui_platform_macos::lifecycle",
                    ?window_id,
                    "platform received open(false), closing native window"
                );
                window.state.request_logical_close();
                if window.native.is_some() {
                    close_native_window(window);
                } else {
                    window.state.finalize_close();
                    emit_closed = true;
                }
            }
        }

        if let Some(visible) = update.visible
            && window.configuration.visible != visible
        {
            window.configuration.visible = visible;
            window.state.open_with_visibility(visible);
            sync_visibility(window);
            emit_visibility = Some(visible);
        }

        if let Some(requested) = update.requested_inner_size {
            if let Some(size) = requested {
                window.configuration.actual_inner_size = size;
                if let Some(native) = window.native.as_mut() {
                    resize_native_window(native, size);
                }
                repaint_retained_scene_for_resize(window_id, window);
                if let Some(active_resize) =
                    window.resize_pipeline.note_resize(window.configuration)
                {
                    trace!(
                        target: "ruin_ui_platform_macos::resize",
                        ?window_id,
                        width = active_resize.actual_inner_size.width,
                        height = active_resize.actual_inner_size.height,
                        scale_factor = active_resize.scale_factor,
                        "emitting Configured from window update resize"
                    );
                    emit_configured = Some(window.configuration);
                } else {
                    trace!(
                        target: "ruin_ui_platform_macos::resize",
                        ?window_id,
                        active_width = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.width),
                        active_height = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.height),
                        next_width = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.width),
                        next_height = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.height),
                        "staged next resize from window update while another layout is in flight"
                    );
                }
            } else {
                emit_configured = Some(window.configuration);
            }
        }
    }

    if emit_opened {
        emit(state, PlatformEvent::Opened { window_id });
    }
    if let Some(visible) = emit_visibility {
        emit(
            state,
            PlatformEvent::VisibilityChanged { window_id, visible },
        );
    }
    if let Some(configuration) = emit_configured {
        emit(
            state,
            PlatformEvent::Configured {
                window_id,
                configuration,
            },
        );
    }
    if emit_closed {
        emit(state, PlatformEvent::Closed { window_id });
    }
}

fn handle_replace_scene(
    state: &Rc<RefCell<MacosState>>,
    window_id: WindowId,
    scene: SceneSnapshot,
) {
    let (presented, followup_events) = {
        let mut state_ref = state.borrow_mut();
        let app = state_ref.app;
        let Some(window) = state_ref.windows.get_mut(&window_id) else {
            return;
        };
        let mut followup_events = Vec::new();

        ensure_native_window(window, app);
        window.latest_scene = Some(scene.clone());
        (
            try_present_scene(window_id, window, &scene, &mut followup_events),
            followup_events,
        )
    };

    for event in followup_events {
        emit(state, event);
    }

    if let Some((scene_version, item_count, visible)) = presented
        && visible
    {
        emit(
            state,
            PlatformEvent::FramePresented {
                window_id,
                scene_version,
                item_count,
            },
        );
    }
}

fn try_present_scene(
    window_id: WindowId,
    window: &mut MacosWindow,
    scene: &SceneSnapshot,
    followup_events: &mut Vec<PlatformEvent>,
) -> Option<(u64, usize, bool)> {
    let current_viewport = window.configuration.actual_inner_size;
    let item_count = scene.item_count();
    let scene_version = scene.version;
    let scene_viewport = scene.logical_size;
    trace!(
        target: "ruin_ui_platform_macos::resize",
        ?window_id,
        scene_width = scene_viewport.width,
        scene_height = scene_viewport.height,
        current_width = current_viewport.width,
        current_height = current_viewport.height,
        active_width = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.width),
        active_height = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.height),
        next_width = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.width),
        next_height = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.height),
        "handle_replace_scene start"
    );
    let render_succeeded = render_scene_to_current_drawable(window_id, window, scene);

    if !render_succeeded {
        return None;
    }

    flush_presented_frame(window);
    window.state.note_presented();
    if let Some(next_resize) = finish_presented_resize(window, scene_viewport) {
        trace!(
            target: "ruin_ui_platform_macos::resize",
            ?window_id,
            scene_width = scene_viewport.width,
            scene_height = scene_viewport.height,
            next_width = next_resize.actual_inner_size.width,
            next_height = next_resize.actual_inner_size.height,
            next_scale = next_resize.scale_factor,
            "presented active resize scene; promoting staged next resize"
        );
        emit_resize_request(window_id, next_resize, followup_events);
    }
    Some((scene_version, item_count, window.configuration.visible))
}

fn repaint_retained_scene_for_resize(window_id: WindowId, window: &mut MacosWindow) {
    if !window.configuration.visible {
        return;
    }
    let Some(scene) = window.latest_scene.clone() else {
        return;
    };

    trace!(
        target: "ruin_ui_platform_macos::resize",
        ?window_id,
        scene_width = scene.logical_size.width,
        scene_height = scene.logical_size.height,
        current_width = window.configuration.actual_inner_size.width,
        current_height = window.configuration.actual_inner_size.height,
        "repainting retained scene into resized drawable"
    );
    if render_scene_to_current_drawable(window_id, window, &scene) {
        flush_presented_frame(window);
        window.state.note_presented();
    }
}

fn render_scene_to_current_drawable(
    window_id: WindowId,
    window: &mut MacosWindow,
    scene: &SceneSnapshot,
) -> bool {
    let current_viewport = window.configuration.actual_inner_size;
    let scene_viewport = scene.logical_size;
    let mut render_succeeded = false;
    if let Some(native) = window.native.as_mut() {
        let view = native.view_object();
        let Some(renderer) = native.renderer.as_mut() else {
            trace!(
                target: "ruin_ui_platform_macos::resize",
                ?window_id,
                "render skipped; no renderer"
            );
            return false;
        };
        configure_view_resize_policy(view);
        renderer.set_scale_factor(window.configuration.scale_factor);
        let scale = window.configuration.scale_factor.max(1.0);
        let drawable_width = (current_viewport.width * scale).max(1.0) as u32;
        let drawable_height = (current_viewport.height * scale).max(1.0) as u32;
        if renderer.size() != (drawable_width, drawable_height) {
            renderer.resize(drawable_width, drawable_height);
        }
        match renderer.render_with_logical_size(scene, current_viewport) {
            Ok(()) => {
                render_succeeded = true;
            }
            Err(RenderError::Lost | RenderError::Outdated) => {
                trace!(
                    target: "ruin_ui_platform_macos::resize",
                    ?window_id,
                    "render lost/outdated; retrying after resize"
                );
                renderer.resize(drawable_width, drawable_height);
                if renderer
                    .render_with_logical_size(scene, current_viewport)
                    .is_ok()
                {
                    render_succeeded = true;
                }
            }
            Err(RenderError::Timeout) => {
                trace!(
                    target: "ruin_ui_platform_macos::resize",
                    ?window_id,
                    "render timeout"
                );
            }
            Err(RenderError::Occluded) => {
                trace!(
                    target: "ruin_ui_platform_macos::resize",
                    ?window_id,
                    "render occluded"
                );
            }
            Err(RenderError::Validation) => {
                trace!(
                    target: "ruin_ui_platform_macos::resize",
                    ?window_id,
                    "render validation failure"
                );
            }
        }
    }
    trace!(
        target: "ruin_ui_platform_macos::resize",
        ?window_id,
        scene_width = scene_viewport.width,
        scene_height = scene_viewport.height,
        active_width = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.width),
        active_height = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.height),
        current_width = current_viewport.width,
        current_height = current_viewport.height,
        render_succeeded,
        "render attempt complete"
    );
    render_succeeded
}

fn ensure_native_window(window: &mut MacosWindow, app: *mut AnyObject) {
    if window.native.is_some() {
        return;
    }

    if let Some(native) = create_native_window(window, app) {
        window.native = Some(native);
    }
}

fn create_native_window(window: &MacosWindow, app: *mut AnyObject) -> Option<NativeWindow> {
    let size = window.configuration.actual_inner_size;
    let rect = CGRect::new(
        CGPoint::new(0.0, 0.0),
        CGSize::new(size.width as f64, size.height as f64),
    );

    let style = NS_WINDOW_STYLE_TITLED
        | NS_WINDOW_STYLE_CLOSABLE
        | NS_WINDOW_STYLE_MINIATURIZABLE
        | NS_WINDOW_STYLE_RESIZABLE;

    let window_obj: *mut AnyObject = unsafe { objc2::msg_send![objc2::class!(NSWindow), alloc] };
    let window_obj: *mut AnyObject = unsafe {
        objc2::msg_send![
            window_obj,
            initWithContentRect: rect,
            styleMask: style,
            backing: NS_BACKING_STORE_BUFFERED,
            defer: false
        ]
    };
    let window_owner = unsafe { ObjcStrong::from_owned(window_obj) }?;
    let window_obj = window_owner.as_ptr();

    let title = NSString::from_str(&window.spec.title);
    unsafe {
        let _: () = objc2::msg_send![window_obj, setTitle: &*title];
        let _: () = objc2::msg_send![window_obj, setReleasedWhenClosed: false];
        let _: () = objc2::msg_send![window_obj, setAcceptsMouseMovedEvents: true];
    }

    let view_class = content_view_class();
    let view_obj: *mut AnyObject = unsafe { objc2::msg_send![view_class, alloc] };
    let view_obj: *mut AnyObject = unsafe { objc2::msg_send![view_obj, initWithFrame: rect] };
    let view_owner = unsafe { ObjcStrong::from_owned(view_obj) }?;
    let view_obj = view_owner.as_ptr();
    let delegate_class = window_delegate_class();
    let delegate_obj: *mut AnyObject = unsafe { objc2::msg_send![delegate_class, new] };
    let delegate_owner = unsafe { ObjcStrong::from_owned(delegate_obj) }?;
    let delegate_obj = delegate_owner.as_ptr();

    unsafe {
        let _: () = objc2::msg_send![
            view_obj,
            setAutoresizingMask: (NS_VIEW_WIDTH_SIZABLE | NS_VIEW_HEIGHT_SIZABLE)
        ];
        let _: () = objc2::msg_send![view_obj, setWantsLayer: Bool::YES];
        configure_view_resize_policy(view_obj);
        let _: () = objc2::msg_send![window_obj, setContentView: view_obj];
        let _: () = objc2::msg_send![window_obj, setDelegate: delegate_obj];
        let _: Bool = objc2::msg_send![window_obj, makeFirstResponder: view_obj];
        let _: () =
            objc2::msg_send![window_obj, makeKeyAndOrderFront: std::ptr::null_mut::<AnyObject>()];
        let _: () = objc2::msg_send![app, activateIgnoringOtherApps: true];
    }
    let metal_layer = configure_metal_surface_layer(view_obj)?;

    let scale_factor: f64 = unsafe { objc2::msg_send![window_obj, backingScaleFactor] };
    let scale_factor = (scale_factor as f32).max(1.0);
    let target = AppKitSurfaceTarget { view: view_obj };
    let renderer = WgpuSceneRenderer::new(
        target,
        (size.width * scale_factor).max(1.0) as u32,
        (size.height * scale_factor).max(1.0) as u32,
        window.spec.base_color,
    )
    .ok();
    let mut renderer = renderer;
    if let Some(renderer) = renderer.as_mut() {
        renderer.set_scale_factor(scale_factor);
    }

    Some(NativeWindow {
        renderer,
        _metal_layer: metal_layer,
        view: view_owner,
        delegate: Some(delegate_owner),
        window: window_owner,
    })
}

fn resize_native_window(native: &mut NativeWindow, size: UiSize) {
    native.set_content_size(size);
}

fn sync_visibility(window: &MacosWindow) {
    let Some(native) = window.native.as_ref() else {
        return;
    };

    if window.configuration.visible {
        native.show();
    } else {
        native.hide();
    }
}

fn close_native_window(window: &mut MacosWindow) {
    if let Some(native) = window.native.as_ref() {
        debug!(
            target: "ruin_ui_platform_macos::lifecycle",
            window_ptr = native.window_ptr(),
            "sending NSWindow close"
        );
        native.close();
    }
}

fn emit(state: &Rc<RefCell<MacosState>>, event: PlatformEvent) {
    let sender = state.borrow().endpoint_events.clone();
    let _ = sender.send(event);
}

fn apply_delegate_events(state: &mut MacosState, pending: &mut Vec<PlatformEvent>) {
    let mut saw_closed = false;
    for resize in take_delegate_resize_events() {
        let Some((window_id, window)) = state.windows.iter_mut().find(|(_, window)| {
            window
                .native
                .as_ref()
                .is_some_and(|native| native.window_ptr() == resize.window_ptr)
        }) else {
            continue;
        };
        if window.state.lifecycle == WindowLifecycle::LogicalClosed {
            continue;
        }
        let resized = resize.size.width > 0.0
            && resize.size.height > 0.0
            && resize.size != window.configuration.actual_inner_size;
        let scale_changed =
            (window.configuration.scale_factor - resize.scale_factor).abs() > f32::EPSILON;
        if !(resized || scale_changed) {
            continue;
        }

        window.configuration.actual_inner_size = resize.size;
        window.configuration.scale_factor = resize.scale_factor.max(1.0);
        note_window_resize(*window_id, window, resize.size, pending);
        repaint_retained_scene_for_resize(*window_id, window);
    }

    for input in take_delegate_input_events() {
        match input {
            DelegateInputEvent::Pointer { window_ptr, event } => {
                let Some((window_id, window)) = state.windows.iter().find(|(_, window)| {
                    window
                        .native
                        .as_ref()
                        .is_some_and(|native| native.window_ptr() == window_ptr)
                }) else {
                    continue;
                };
                if window.state.lifecycle != WindowLifecycle::LogicalClosed {
                    pending.push(PlatformEvent::Pointer {
                        window_id: *window_id,
                        event,
                    });
                }
            }
            DelegateInputEvent::Keyboard { window_ptr, event } => {
                let Some((window_id, window)) = state.windows.iter().find(|(_, window)| {
                    window
                        .native
                        .as_ref()
                        .is_some_and(|native| native.window_ptr() == window_ptr)
                }) else {
                    continue;
                };
                if window.state.lifecycle != WindowLifecycle::LogicalClosed {
                    pending.push(PlatformEvent::Keyboard {
                        window_id: *window_id,
                        event,
                    });
                }
            }
        }
    }

    for window_ptr in take_delegate_close_requests() {
        let Some((window_id, window)) = state.windows.iter_mut().find(|(_, window)| {
            window
                .native
                .as_ref()
                .is_some_and(|native| native.window_ptr() == window_ptr)
        }) else {
            continue;
        };
        if window.state.note_delegate_close_request() {
            debug!(
                target: "ruin_ui_platform_macos::lifecycle",
                ?window_id,
                "emitting CloseRequested"
            );
            pending.push(PlatformEvent::CloseRequested {
                window_id: *window_id,
            });
        }
    }

    for window_ptr in take_delegate_closed_windows() {
        let Some((window_id, window)) = state.windows.iter_mut().find(|(_, window)| {
            window
                .native
                .as_ref()
                .is_some_and(|native| native.window_ptr() == window_ptr)
        }) else {
            continue;
        };
        finalize_native_close(*window_id, window, pending);
        saw_closed = true;
    }

    if saw_closed
        && state
            .windows
            .values()
            .all(|window| window.state.lifecycle == WindowLifecycle::LogicalClosed)
    {
        debug!(
            target: "ruin_ui_platform_macos::lifecycle",
            "all windows closed; terminating NSApplication"
        );
        unsafe {
            let _: () = objc2::msg_send![state.app, terminate: std::ptr::null_mut::<AnyObject>()];
        }
    }
}

fn note_window_resize(
    window_id: WindowId,
    window: &mut MacosWindow,
    viewport: UiSize,
    pending: &mut Vec<PlatformEvent>,
) {
    debug_assert_eq!(window.configuration.actual_inner_size, viewport);
    if let Some(active_resize) = window.resize_pipeline.note_resize(window.configuration) {
        emit_resize_request(window_id, active_resize, pending);
    } else {
        trace!(
            target: "ruin_ui_platform_macos::resize",
            ?window_id,
            active_width = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.width),
            active_height = window.resize_pipeline.active_resize.map(|s| s.actual_inner_size.height),
            active_scale = window.resize_pipeline.active_resize.map(|s| s.scale_factor),
            next_width = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.width),
            next_height = window.resize_pipeline.next_resize.map(|s| s.actual_inner_size.height),
            next_scale = window.resize_pipeline.next_resize.map(|s| s.scale_factor),
            "staged next configuration while another layout is in flight"
        );
    }
}

fn emit_resize_request(
    window_id: WindowId,
    configuration: WindowConfigured,
    pending: &mut Vec<PlatformEvent>,
) {
    pending.push(PlatformEvent::Configured {
        window_id,
        configuration,
    });
    trace!(
        target: "ruin_ui_platform_macos::resize",
        ?window_id,
        width = configuration.actual_inner_size.width,
        height = configuration.actual_inner_size.height,
        scale_factor = configuration.scale_factor,
        "emitting Configured from platform configuration change"
    );
}

fn finish_presented_resize(
    window: &mut MacosWindow,
    presented: UiSize,
) -> Option<WindowConfigured> {
    window.resize_pipeline.note_presented_scene(presented)
}

fn presentation_flush_policy(
    has_presented_frame: bool,
    in_live_resize: bool,
) -> PresentationFlushPolicy {
    if in_live_resize {
        PresentationFlushPolicy::DisplayAndTransaction
    } else if !has_presented_frame {
        PresentationFlushPolicy::DisplayIfNeeded
    } else {
        PresentationFlushPolicy::None
    }
}

fn configure_metal_surface_layer(view: *mut AnyObject) -> Option<Retained<CAMetalLayer>> {
    let view_ptr = NonNull::new(view.cast::<c_void>())?;
    let layer = unsafe { MetalLayer::from_ns_view(view_ptr) };
    let layer_ptr = layer.into_raw().as_ptr().cast::<CAMetalLayer>();
    let layer = unsafe { Retained::from_raw(layer_ptr) }?;
    layer.setContentsGravity(unsafe { kCAGravityTopLeft });
    layer.setPresentsWithTransaction(false);
    Some(layer)
}

fn flush_presented_frame(window: &MacosWindow) {
    let Some(native) = window.native.as_ref() else {
        return;
    };
    let in_live_resize = native.in_live_resize();
    let policy = presentation_flush_policy(window.state.has_presented_frame, in_live_resize);
    match policy {
        PresentationFlushPolicy::None => {}
        PresentationFlushPolicy::DisplayIfNeeded => native.display_if_needed(),
        PresentationFlushPolicy::DisplayAndTransaction => {
            native.display_if_needed();
            native.flush_core_animation_transaction();
        }
    }
    trace!(
        target: "ruin_ui_platform_macos::resize",
        in_live_resize,
        ?policy,
        "applied presentation flush policy after presented frame"
    );
}

fn configure_view_resize_policy(view: *mut AnyObject) {
    if view.is_null() {
        return;
    }
    unsafe {
        let can_set_redraw: bool =
            objc2::msg_send![view, respondsToSelector: sel!(setLayerContentsRedrawPolicy:)];
        if can_set_redraw {
            let _: () = objc2::msg_send![
                view,
                setLayerContentsRedrawPolicy: NS_VIEW_LAYER_CONTENTS_REDRAW_DURING_VIEW_RESIZE
            ];
        }
        let can_set_placement: bool =
            objc2::msg_send![view, respondsToSelector: sel!(setLayerContentsPlacement:)];
        if can_set_placement {
            let _: () = objc2::msg_send![
                view,
                setLayerContentsPlacement: NS_VIEW_LAYER_CONTENTS_PLACEMENT_TOP_LEFT
            ];
        }
        let layer: *mut AnyObject = objc2::msg_send![view, layer];
        if !layer.is_null() {
            let can_set_gravity: bool =
                objc2::msg_send![layer, respondsToSelector: sel!(setContentsGravity:)];
            if can_set_gravity {
                let _: () = objc2::msg_send![layer, setContentsGravity: ns_string!("topLeft")];
            }
            let can_set_needs_display: bool =
                objc2::msg_send![layer, respondsToSelector: sel!(setNeedsDisplayOnBoundsChange:)];
            if can_set_needs_display {
                let _: () = objc2::msg_send![layer, setNeedsDisplayOnBoundsChange: false];
            }
        }
    }
}

fn collect_window_state_events(
    window_id: WindowId,
    window: &mut MacosWindow,
    pending: &mut Vec<PlatformEvent>,
) {
    let Some((window_ptr, view_ptr)) = window
        .native
        .as_ref()
        .map(|native| (native.window_object(), native.view_object()))
    else {
        return;
    };
    if window.state.lifecycle == WindowLifecycle::LogicalClosed {
        return;
    }

    let frame: CGRect = unsafe { objc2::msg_send![view_ptr, frame] };
    let scale_factor: f64 = unsafe { objc2::msg_send![window_ptr, backingScaleFactor] };
    let scale_factor = scale_factor as f32;
    let content_size = UiSize::new(frame.size.width as f32, frame.size.height as f32);
    let resized = content_size.width > 0.0
        && content_size.height > 0.0
        && content_size != window.configuration.actual_inner_size;
    let scale_changed = (window.configuration.scale_factor - scale_factor).abs() > f32::EPSILON;

    if resized || scale_changed {
        window.configuration.actual_inner_size = content_size;
        window.configuration.scale_factor = scale_factor.max(1.0);
        note_window_resize(window_id, window, content_size, pending);
    }

    let is_visible = native_window_is_logically_visible(window_ptr);
    let became_visible = is_visible && !window.configuration.visible;
    if is_visible != window.configuration.visible {
        window.configuration.visible = is_visible;
        window.state.set_native_visibility(is_visible);
        pending.push(PlatformEvent::VisibilityChanged {
            window_id,
            visible: is_visible,
        });
    }

    if window.state.close_in_progress && !is_visible {
        finalize_native_close(window_id, window, pending);
        return;
    }

    if is_visible
        && let Some(scene) = window.latest_scene.clone()
        && (became_visible
            || !window.state.has_presented_frame
            || window
                .resize_pipeline
                .active_resize
                .is_some_and(|configuration| configuration.actual_inner_size == scene.logical_size))
    {
        let _ = try_present_scene(window_id, window, &scene, pending);
    } else if is_visible
        && !window.state.has_presented_frame
        && let Some(initial_viewport) = window
            .resize_pipeline
            .ensure_initial_request(window.configuration)
    {
        emit_resize_request(window_id, initial_viewport, pending);
    }
}

fn native_window_is_logically_visible(window: *mut AnyObject) -> bool {
    let is_visible: bool = unsafe { objc2::msg_send![window, isVisible] };
    let is_miniaturized: bool = unsafe { objc2::msg_send![window, isMiniaturized] };
    is_visible && !is_miniaturized
}

fn keyboard_modifiers(event: *mut AnyObject) -> KeyboardModifiers {
    let flags: usize = unsafe { objc2::msg_send![event, modifierFlags] };
    KeyboardModifiers {
        shift: flags & NSEVENT_MODIFIER_FLAG_SHIFT != 0,
        control: flags & NSEVENT_MODIFIER_FLAG_CONTROL != 0,
        alt: flags & NSEVENT_MODIFIER_FLAG_OPTION != 0,
        super_key: flags & NSEVENT_MODIFIER_FLAG_COMMAND != 0,
    }
}

fn keyboard_key(keycode: u16, text: Option<&str>) -> KeyboardKey {
    match keycode {
        36 => KeyboardKey::Enter,
        48 => KeyboardKey::Tab,
        53 => KeyboardKey::Escape,
        51 => KeyboardKey::Backspace,
        117 => KeyboardKey::Delete,
        123 => KeyboardKey::ArrowLeft,
        124 => KeyboardKey::ArrowRight,
        125 => KeyboardKey::ArrowDown,
        126 => KeyboardKey::ArrowUp,
        115 => KeyboardKey::Home,
        119 => KeyboardKey::End,
        _ => text
            .map(str::trim)
            .filter(|value| !value.is_empty())
            .map(ToOwned::to_owned)
            .map(KeyboardKey::Character)
            .unwrap_or(KeyboardKey::Unknown),
    }
}

fn event_characters(event: *mut AnyObject) -> Option<String> {
    let string: *mut AnyObject = unsafe { objc2::msg_send![event, characters] };
    nsstring_to_string(string)
}

fn nsstring_to_string(string: *mut AnyObject) -> Option<String> {
    if string.is_null() {
        return None;
    }
    let cstr_ptr: *const i8 = unsafe { objc2::msg_send![string, UTF8String] };
    if cstr_ptr.is_null() {
        return None;
    }
    let text = unsafe { CStr::from_ptr(cstr_ptr) }
        .to_string_lossy()
        .into_owned();
    (!text.is_empty()).then_some(text)
}

fn apply_cursor_icon(cursor: CursorIcon) {
    let class = objc2::class!(NSCursor);
    let cursor_obj: *mut AnyObject = unsafe {
        match cursor {
            CursorIcon::Default => objc2::msg_send![class, arrowCursor],
            CursorIcon::Pointer => objc2::msg_send![class, pointingHandCursor],
            CursorIcon::Text => objc2::msg_send![class, IBeamCursor],
        }
    };
    if cursor_obj.is_null() {
        return;
    }
    unsafe {
        let _: () = objc2::msg_send![cursor_obj, set];
    }
}

fn finalize_native_close(
    window_id: WindowId,
    window: &mut MacosWindow,
    pending: &mut Vec<PlatformEvent>,
) {
    debug!(
        target: "ruin_ui_platform_macos::lifecycle",
        ?window_id,
        "finalizing native close"
    );
    let should_emit_closed = window.state.lifecycle != WindowLifecycle::LogicalClosed;
    if let Some(mut native) = window.native.take() {
        native.detach_delegate();
    }

    window.state.finalize_close();

    if window.configuration.visible {
        window.configuration.visible = false;
        pending.push(PlatformEvent::VisibilityChanged {
            window_id,
            visible: false,
        });
    }
    if should_emit_closed {
        debug!(
            target: "ruin_ui_platform_macos::lifecycle",
            ?window_id,
            "emitting Closed"
        );
        pending.push(PlatformEvent::Closed { window_id });
    }
}

fn sync_window_metrics(window: &mut MacosWindow) {
    let Some((window_ptr, view_ptr)) = window
        .native
        .as_ref()
        .map(|native| (native.window_object(), native.view_object()))
    else {
        return;
    };
    let frame: CGRect = unsafe { objc2::msg_send![view_ptr, frame] };
    let scale_factor: f64 = unsafe { objc2::msg_send![window_ptr, backingScaleFactor] };
    window.configuration.actual_inner_size =
        UiSize::new(frame.size.width as f32, frame.size.height as f32);
    window.configuration.scale_factor = (scale_factor as f32).max(1.0);
}

#[cfg(test)]
mod tests {
    use super::{
        MacosWindowState, PresentationFlushPolicy, ResizePipeline, presentation_flush_policy,
    };
    use ruin_ui::{UiSize, WindowConfigured, WindowLifecycle};

    fn configuration(width: f32, height: f32, scale_factor: f32) -> WindowConfigured {
        WindowConfigured {
            actual_inner_size: UiSize::new(width, height),
            scale_factor,
            visible: true,
            maximized: false,
            fullscreen: false,
        }
    }

    #[test]
    fn resize_pipeline_stages_latest_resize_while_active_request_is_in_flight() {
        let mut pipeline = ResizePipeline::default();
        let first = configuration(640.0, 480.0, 1.0);
        let second = configuration(800.0, 600.0, 1.0);
        let third = configuration(1024.0, 768.0, 1.0);

        assert_eq!(pipeline.note_resize(first), Some(first));
        assert_eq!(pipeline.active_resize, Some(first));
        assert_eq!(pipeline.next_resize, None);

        assert_eq!(pipeline.note_resize(second), None);
        assert_eq!(pipeline.active_resize, Some(first));
        assert_eq!(pipeline.next_resize, Some(second));

        assert_eq!(pipeline.note_resize(third), None);
        assert_eq!(pipeline.active_resize, Some(first));
        assert_eq!(pipeline.next_resize, Some(third));
    }

    #[test]
    fn resize_pipeline_promotes_next_resize_after_presenting_active_scene() {
        let mut pipeline = ResizePipeline::default();
        let first = configuration(640.0, 480.0, 1.0);
        let second = configuration(1024.0, 768.0, 1.0);

        assert_eq!(pipeline.note_resize(first), Some(first));
        assert_eq!(pipeline.note_resize(second), None);

        assert_eq!(
            pipeline.note_presented_scene(first.actual_inner_size),
            Some(second)
        );
        assert_eq!(pipeline.active_resize, Some(second));
        assert_eq!(pipeline.next_resize, None);
    }

    #[test]
    fn resize_pipeline_drops_duplicate_staged_resize_when_present_already_satisfies_it() {
        let mut pipeline = ResizePipeline::default();
        let viewport = configuration(800.0, 600.0, 1.0);

        assert_eq!(pipeline.note_resize(viewport), Some(viewport));
        assert_eq!(pipeline.note_resize(viewport), None);

        assert_eq!(
            pipeline.note_presented_scene(viewport.actual_inner_size),
            None
        );
        assert_eq!(pipeline.active_resize, None);
        assert_eq!(pipeline.next_resize, None);
    }

    #[test]
    fn resize_pipeline_preserves_same_size_scale_changes() {
        let mut pipeline = ResizePipeline::default();
        let one_x = configuration(800.0, 600.0, 1.0);
        let two_x = configuration(800.0, 600.0, 2.0);

        assert_eq!(pipeline.note_resize(one_x), Some(one_x));
        assert_eq!(pipeline.note_resize(two_x), None);

        assert_eq!(
            pipeline.note_presented_scene(one_x.actual_inner_size),
            Some(two_x)
        );
        assert_eq!(pipeline.active_resize, Some(two_x));
        assert_eq!(pipeline.next_resize, None);
    }

    #[test]
    fn resize_pipeline_ignores_presents_for_non_active_scene_sizes() {
        let mut pipeline = ResizePipeline::default();
        let active = configuration(640.0, 480.0, 1.0);
        let next = configuration(900.0, 700.0, 1.0);

        assert_eq!(pipeline.note_resize(active), Some(active));
        assert_eq!(pipeline.note_resize(next), None);

        assert_eq!(
            pipeline.note_presented_scene(UiSize::new(700.0, 500.0)),
            None
        );
        assert_eq!(pipeline.active_resize, Some(active));
        assert_eq!(pipeline.next_resize, Some(next));
    }

    #[test]
    fn presentation_flush_policy_is_narrowed_to_first_present_and_live_resize() {
        assert_eq!(
            presentation_flush_policy(false, false),
            PresentationFlushPolicy::DisplayIfNeeded
        );
        assert_eq!(
            presentation_flush_policy(true, false),
            PresentationFlushPolicy::None
        );
        assert_eq!(
            presentation_flush_policy(true, true),
            PresentationFlushPolicy::DisplayAndTransaction
        );
    }

    #[test]
    fn window_state_distinguishes_hide_from_close() {
        let mut state = MacosWindowState::new(true);
        state.open_with_visibility(true);
        state.note_presented();

        state.set_native_visibility(false);
        assert_eq!(state.lifecycle, WindowLifecycle::OpenHidden);
        assert!(state.desired_visible);
        assert!(!state.close_in_progress);
        assert!(!state.has_presented_frame);

        state.set_native_visibility(true);
        assert_eq!(state.lifecycle, WindowLifecycle::OpenVisible);
        assert!(state.desired_visible);
    }

    #[test]
    fn window_state_close_request_is_single_shot_and_finalize_resets_state() {
        let mut state = MacosWindowState::new(true);
        state.open_with_visibility(true);

        assert!(state.mark_close_requested_sent());
        assert!(!state.mark_close_requested_sent());

        state.request_logical_close();
        assert!(!state.desired_visible);
        assert!(state.close_in_progress);

        state.finalize_close();
        assert_eq!(state.lifecycle, WindowLifecycle::LogicalClosed);
        assert!(!state.desired_visible);
        assert!(!state.close_in_progress);
        assert!(!state.has_presented_frame);
    }

    #[test]
    fn window_state_app_requested_close_suppresses_close_requested_event() {
        let mut state = MacosWindowState::new(true);
        state.open_with_visibility(true);

        state.request_logical_close();

        assert!(state.close_in_progress);
        assert!(!state.note_delegate_close_request());
        assert!(!state.close_requested_sent);
    }
}
