//! DXE Core Events
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!
use core::{
    ffi::c_void,
    sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering},
};

use r_efi::efi;

use patina::pi::protocols::timer;

use patina_internal_cpu::{cpu::EfiCpu, interrupts};

use crate::{
    event_db::{SpinLockedEventDb, TimerDelay},
    gcd,
    protocols::PROTOCOL_DB,
    systemtables::EfiSystemTable,
};

pub static EVENT_DB: SpinLockedEventDb = SpinLockedEventDb::new();

static CURRENT_TPL: AtomicUsize = AtomicUsize::new(efi::TPL_APPLICATION);
static SYSTEM_TIME: AtomicU64 = AtomicU64::new(0);

/// # Safety
///
/// Caller must ensure that `event` points to valid writable memory. Only a null
/// check is performed here; no other validation of the pointer is done.
unsafe extern "efiapi" fn create_event(
    event_type: u32,
    notify_tpl: efi::Tpl,
    notify_function: Option<efi::EventNotify>,
    notify_context: *mut c_void,
    event: *mut efi::Event,
) -> efi::Status {
    if event.is_null() {
        return efi::Status::INVALID_PARAMETER;
    }

    let notify_context = if !notify_context.is_null() { Some(notify_context) } else { None };

    let (event_type, event_group) = match event_type {
        efi::EVT_SIGNAL_EXIT_BOOT_SERVICES => (efi::EVT_NOTIFY_SIGNAL, Some(efi::EVENT_GROUP_EXIT_BOOT_SERVICES)),
        efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE => {
            (efi::EVT_NOTIFY_SIGNAL, Some(efi::EVENT_GROUP_VIRTUAL_ADDRESS_CHANGE))
        }
        other => (other, None),
    };

    match EVENT_DB.create_event(event_type, notify_tpl, notify_function, notify_context, event_group) {
        Ok(new_event) => {
            // SAFETY: caller must ensure that event is a valid pointer. It is null-checked above.
            unsafe { event.write_unaligned(new_event) };
            efi::Status::SUCCESS
        }
        Err(err) => err.into(),
    }
}

/// # Safety
///
/// Caller must ensure that `event` points to valid writable memory. Only a null
/// check is performed here; no other validation of the pointer is done.
unsafe extern "efiapi" fn create_event_ex(
    event_type: u32,
    notify_tpl: efi::Tpl,
    notify_function: Option<efi::EventNotify>,
    notify_context: *const c_void,
    event_group: *const efi::Guid,
    event: *mut efi::Event,
) -> efi::Status {
    if event.is_null() {
        return efi::Status::INVALID_PARAMETER;
    }

    let notify_context = if !notify_context.is_null() { Some(notify_context as *mut c_void) } else { None };

    match event_type {
        efi::EVT_SIGNAL_EXIT_BOOT_SERVICES | efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE => {
            return efi::Status::INVALID_PARAMETER;
        }
        _ => (),
    }

    // SAFETY: caller must ensure that event_group is a valid pointer if not null.
    let event_group = if !event_group.is_null() { Some(unsafe { event_group.read_unaligned() }) } else { None };

    match EVENT_DB.create_event(event_type, notify_tpl, notify_function, notify_context, event_group) {
        Ok(new_event) => {
            // SAFETY: caller must ensure that event is a valid pointer. It is null-checked above.
            unsafe { event.write_unaligned(new_event) };
            efi::Status::SUCCESS
        }
        Err(err) => err.into(),
    }
}

/// Closes an event from the event database.
///
/// This function is safe to call with an unverified `event` parameter. The
/// underlying implementation looks up the event by ID in the database and
/// returns a failure status if it is not found. No incoming pointers are
/// dereferenced, so this is not marked `unsafe`.
pub extern "efiapi" fn close_event(event: efi::Event) -> efi::Status {
    match EVENT_DB.close_event(event) {
        Ok(()) => efi::Status::SUCCESS,
        Err(err) => err.into(),
    }
}

/// Signals an event in the event database.
///
/// This function is safe to call with an unverified `event` parameter. The
/// underlying implementation looks up the event by ID in the database and
/// returns a failure status if it is not found. No incoming pointers are
/// dereferenced, so this is not marked `unsafe`.
pub extern "efiapi" fn signal_event(event: efi::Event) -> efi::Status {
    // Note: The C-reference implementation of SignalEvent gets an immediate dispatch of
    // pending events as a side effect of the locking implementation calling raise/restore
    // TPL. This will occur when the event lock is dropped at the end of signal_event().
    match EVENT_DB.signal_event(event) {
        Ok(()) => efi::Status::SUCCESS,
        Err(err) => err.into(),
    }
}

/// # Safety
///
/// Caller must ensure that `event_array` points to a valid array of at least
/// `number_of_events` `efi::Event` entries. `number_of_events` must not exceed
/// the actual length of the array; otherwise out-of-bounds reads will occur. If
/// `out_index` is non-null, it must point to valid writable memory. Only a null
/// check is performed on `event_array`; no other validation of either pointer
/// is done.
unsafe extern "efiapi" fn wait_for_event(
    number_of_events: usize,
    event_array: *mut efi::Event,
    out_index: *mut usize,
) -> efi::Status {
    if number_of_events == 0 || event_array.is_null() {
        return efi::Status::INVALID_PARAMETER;
    }

    if CURRENT_TPL.load(Ordering::SeqCst) != efi::TPL_APPLICATION {
        return efi::Status::UNSUPPORTED;
    }

    //spin on the list
    loop {
        let mut event_ptr = event_array;
        for index in 0..number_of_events {
            // SAFETY: caller must ensure that event_array is a valid pointer and number_of_events is correct. event_array is null-checked above.
            let event = unsafe { event_ptr.read_unaligned() };
            match check_event(event) {
                efi::Status::NOT_READY => (),
                status => {
                    // SAFETY: caller must ensure that out_index is a valid pointer if it is not null.
                    if !out_index.is_null() {
                        // SAFETY: out_index is non-null and points to writable memory.
                        unsafe {
                            out_index.write_unaligned(index);
                        };
                    }
                    return status;
                }
            }
            // SAFETY: caller must ensure that event_array is a valid pointer and number_of_events is correct. event_array is null-checked above.
            event_ptr = unsafe { event_ptr.add(1) };
        }

        // EDK2 core signals an idle event here to notify an event group of the "idle" state. The only consumers of that
        // event are the CPU architectural drivers which use it to enter a low power state until the next interrupt.
        // Patina implements CPU architectural support as part of the core, so directly call the sleep() method to avoid
        // exposing the idle event to outside consumers (this event group is not specified in UEFI or PI specs). In the
        // event that a need arises to expose the idle event to consumers outside of Patina, it can be signaled here.
        EfiCpu::sleep();
    }
}

/// Checks whether an event is in the signaled state.
///
/// This function is safe to call with an unverified `event` parameter. The
/// underlying implementation looks up the event by ID in the database and
/// returns a failure status if it is not found. No incoming pointers are
/// dereferenced, so this is not marked `unsafe`.
pub extern "efiapi" fn check_event(event: efi::Event) -> efi::Status {
    let event_type = match EVENT_DB.get_event_type(event) {
        Ok(event_type) => event_type,
        Err(err) => return err.into(),
    };

    if event_type.is_notify_signal() {
        return efi::Status::INVALID_PARAMETER;
    }

    match EVENT_DB.read_and_clear_signaled(event) {
        Ok(signaled) => {
            if signaled {
                return efi::Status::SUCCESS;
            }
        }
        Err(err) => return err.into(),
    }

    match EVENT_DB.queue_event_notify(event) {
        Ok(()) => (),
        Err(err) => return err.into(),
    }

    // raise/restore TPL to allow notifies to occur at the appropriate level.
    let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
    restore_tpl(old_tpl);

    match EVENT_DB.read_and_clear_signaled(event) {
        Ok(signaled) => {
            if signaled {
                return efi::Status::SUCCESS;
            }
        }
        Err(err) => return err.into(),
    }

    efi::Status::NOT_READY
}

/// Sets a timer on an event in the event database.
///
/// This function is safe to call with an unverified `event` parameter. The
/// underlying implementation looks up the event by ID in the database and
/// returns a failure status if it is not found. No incoming pointers are
/// dereferenced, so this is not marked `unsafe`.
pub extern "efiapi" fn set_timer(event: efi::Event, timer_type: efi::TimerDelay, trigger_time: u64) -> efi::Status {
    let timer_type = match TimerDelay::try_from(timer_type) {
        Err(err) => return err,
        Ok(timer_type) => timer_type,
    };

    let (trigger_time, period) = match timer_type {
        TimerDelay::Cancel => (None, None),
        TimerDelay::Relative => (Some(SYSTEM_TIME.load(Ordering::SeqCst) + trigger_time), None),
        TimerDelay::Periodic => (Some(SYSTEM_TIME.load(Ordering::SeqCst) + trigger_time), Some(trigger_time)),
    };

    match EVENT_DB.set_timer(event, timer_type, trigger_time, period) {
        Ok(()) => efi::Status::SUCCESS,
        Err(err) => err.into(),
    }
}

/// Raises the task priority level (TPL) to the specified value.
///
/// This function is safe to call with any `new_tpl` value. It validates that
/// the requested TPL does not exceed `TPL_HIGH_LEVEL` and is not lower than the
/// current TPL, panicking on violation. No incoming parameters are dereferenced,
/// so this is not marked `unsafe`.
pub extern "efiapi" fn raise_tpl(new_tpl: efi::Tpl) -> efi::Tpl {
    if new_tpl > efi::TPL_HIGH_LEVEL {
        panic!("Invalid attempt to raise TPL above TPL_HIGH_LEVEL: {new_tpl:#x?}");
    }

    let prev_tpl = CURRENT_TPL.fetch_max(new_tpl, Ordering::SeqCst);

    if new_tpl < prev_tpl {
        panic!("Invalid attempt to raise TPL to lower value. New TPL: {new_tpl:#x?}, Prev TPL: {prev_tpl:#x?}");
    }

    if (new_tpl == efi::TPL_HIGH_LEVEL) && (prev_tpl < efi::TPL_HIGH_LEVEL) {
        interrupts::disable_interrupts();
    }
    prev_tpl
}

/// Restores the task priority level (TPL) to the specified value.
///
/// This function is safe to call with any `new_tpl` value. It validates that
/// the requested TPL is not higher than the current TPL, panicking on violation.
/// Pending event notifications at higher TPLs are dispatched before returning.
/// No incoming parameters are dereferenced, so this is not marked `unsafe`.
pub extern "efiapi" fn restore_tpl(new_tpl: efi::Tpl) {
    let prev_tpl = CURRENT_TPL.fetch_min(new_tpl, Ordering::SeqCst);

    if new_tpl > prev_tpl {
        panic!("Invalid attempt to restore TPL to higher value. New TPL: {new_tpl:#x?}, Prev TPL: {prev_tpl:#x?}");
    }

    if new_tpl < prev_tpl {
        // loop over any pending event notifications. Note: more notifications can be queued in the course of servicing
        // the current set of notifies; this will continue looping as long as there are any pending notifications, even
        // if they were queued after the loop started.
        loop {
            // Care must be taken to deal with reentrant "restore_tpl" cases. For example, the consume_next_event_notify
            // call requires taking the lock on EVENT_DB to retrieve the next notification. The release of that lock will
            // call restore_tpl. To avoid infinite recursion, this logic uses EVENT_NOTIFIES_IN_PROGRESS as a flag to
            // avoid reentrancy in the specific case that the lock is being taken for the purpose of acquiring event
            // notifies.
            static EVENT_NOTIFIES_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
            let event =
                match EVENT_NOTIFIES_IN_PROGRESS.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {
                    Ok(_) => {
                        let result = EVENT_DB.consume_next_event_notify(new_tpl);
                        EVENT_NOTIFIES_IN_PROGRESS.store(false, Ordering::Release);
                        result
                    }
                    _ => break, /* reentrant restore_tpl case */
                };

            let Some(event) = event else {
                break; /* no pending events */
            };
            if event.notify_tpl < efi::TPL_HIGH_LEVEL {
                interrupts::enable_interrupts();
            } else {
                interrupts::disable_interrupts();
            }
            CURRENT_TPL.store(event.notify_tpl, Ordering::SeqCst);
            let notify_context = event.notify_context.unwrap_or(core::ptr::null_mut());

            if EVENT_DB.get_event_type(event.event).unwrap().is_notify_signal() {
                let _ = EVENT_DB.clear_signal(event.event);
            }

            if let Some(notify_function) = event.notify_function {
                // SAFETY: Marking the call as unsafe as per r-efi definition because the function
                // pointer could point to arbitrary code outside the control of DXE Rust.
                unsafe { notify_function(event.event, notify_context) };
            }
        }
    }

    CURRENT_TPL.store(new_tpl, Ordering::SeqCst);

    if new_tpl < efi::TPL_HIGH_LEVEL {
        interrupts::enable_interrupts();
    }
}

extern "efiapi" fn timer_tick(time: u64) {
    let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
    SYSTEM_TIME.fetch_add(time, Ordering::SeqCst);
    let current_time = SYSTEM_TIME.load(Ordering::SeqCst);
    EVENT_DB.timer_tick(current_time);
    restore_tpl(old_tpl); //implicitly dispatches timer notifies if any.
}

extern "efiapi" fn timer_available_callback(event: efi::Event, _context: *mut c_void) {
    match PROTOCOL_DB.locate_protocol(timer::PROTOCOL_GUID.into_inner()) {
        Ok(timer_arch_ptr) => {
            let timer_arch_ptr = timer_arch_ptr as *mut timer::Protocol;
            // SAFETY: timer_arch_ptr was successfully returned from locate_protocol.
            let timer_arch = unsafe { &*(timer_arch_ptr) };
            (timer_arch.register_handler)(timer_arch_ptr, timer_tick);
            if let Err(status_err) = EVENT_DB.close_event(event) {
                log::warn!("Could not close event for timer_available_callback due to error {status_err:?}");
            }
        }
        Err(err) => panic!("Unable to locate timer arch: {err:?}"),
    }
}

/// This callback is invoked whenever the GCD changes, and will signal the required UEFI event group.
pub fn gcd_map_change(map_change_type: gcd::MapChangeType) {
    match map_change_type {
        gcd::MapChangeType::AddMemorySpace
        | gcd::MapChangeType::AllocateMemorySpace
        | gcd::MapChangeType::FreeMemorySpace
        | gcd::MapChangeType::RemoveMemorySpace
        | gcd::MapChangeType::SetMemoryCapabilities => EVENT_DB.signal_group(efi::EVENT_GROUP_MEMORY_MAP_CHANGE),
        gcd::MapChangeType::SetMemoryAttributes => (),
    }
}

pub fn init_events_support(st: &mut EfiSystemTable) {
    let mut bs = st.boot_services().get();
    bs.create_event = create_event;
    bs.create_event_ex = create_event_ex;
    bs.close_event = close_event;
    bs.signal_event = signal_event;
    bs.wait_for_event = wait_for_event;
    bs.check_event = check_event;
    bs.set_timer = set_timer;
    bs.raise_tpl = raise_tpl;
    bs.restore_tpl = restore_tpl;
    st.boot_services().set(bs);

    //set up call back for timer arch protocol installation.
    let event = EVENT_DB
        .create_event(efi::EVT_NOTIFY_SIGNAL, efi::TPL_CALLBACK, Some(timer_available_callback), None, None)
        .expect("Failed to create timer available callback.");

    PROTOCOL_DB
        .register_protocol_notify(timer::PROTOCOL_GUID.into_inner(), event)
        .expect("Failed to register protocol notify on timer arch callback.");
}

#[cfg(test)]
#[coverage(off)]
mod tests {
    use super::*;
    use crate::test_support;
    use std::{ptr, sync::atomic::Ordering};

    fn with_locked_state<F: Fn() + std::panic::RefUnwindSafe>(f: F) {
        test_support::with_global_lock(|| {
            test_support::init_test_logger();
            // SAFETY: Test-only initialization of global services under the global lock.
            unsafe {
                crate::test_support::init_test_gcd(None);
                crate::test_support::reset_allocators();
                crate::test_support::init_test_protocol_db();
            }

            let _guard = test_support::StateGuard::new(|| {
                // SAFETY: Cleanup code runs with global lock held, resetting
                // global state that was initialized above.
                unsafe {
                    crate::GCD.reset();
                    crate::PROTOCOL_DB.reset();
                    crate::allocator::reset_allocators();
                }
            });

            f();
        })
        .unwrap();
    }

    extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {}

    // Track if notification was called
    static NOTIFY_CALLED: AtomicBool = AtomicBool::new(false);
    extern "efiapi" fn tracking_notify(_event: efi::Event, _context: *mut c_void) {
        NOTIFY_CALLED.store(true, Ordering::SeqCst);
    }

    #[test]
    fn test_create_event_null_event_pointer() {
        with_locked_state(|| {
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe { create_event(0, efi::TPL_APPLICATION, None, ptr::null_mut(), ptr::null_mut()) };

            assert_eq!(result, efi::Status::INVALID_PARAMETER);
        });
    }

    #[test]
    fn test_create_event_success() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe { create_event(0, efi::TPL_APPLICATION, None, ptr::null_mut(), &mut event) };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_with_notify_context() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let context = Box::into_raw(Box::new(42)) as *mut c_void;
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe { create_event(0, efi::TPL_APPLICATION, None, context, &mut event) };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_with_notify_function() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_CALLBACK, notify_fn, ptr::null_mut(), &mut event)
            };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_virtual_address_change() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();

            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
                    efi::TPL_CALLBACK,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_exit_boot_services() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();

            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_SIGNAL_EXIT_BOOT_SERVICES,
                    efi::TPL_CALLBACK,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_ex_null_event() {
        with_locked_state(|| {
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result =
                unsafe { create_event_ex(0, efi::TPL_APPLICATION, None, ptr::null(), ptr::null(), ptr::null_mut()) };

            assert_eq!(result, efi::Status::INVALID_PARAMETER);
        });
    }

    #[test]
    fn test_create_event_ex_with_event_group() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let event_guid: efi::Guid = patina::BinaryGuid::from_string("87A2E5D9-C34F-4B21-8E57-1AF93C82D76B").into();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event_ex(
                    efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_CALLBACK,
                    notify_fn,
                    ptr::null(),
                    &event_guid,
                    &mut event,
                )
            };

            assert_eq!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_create_event_ex_exit_boot_services() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            // EVT_SIGNAL_EXIT_BOOT_SERVICES should fail with create_event_ex
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event_ex(
                    efi::EVT_SIGNAL_EXIT_BOOT_SERVICES,
                    efi::TPL_CALLBACK,
                    Some(test_notify),
                    ptr::null(),
                    ptr::null(),
                    &mut event,
                )
            };

            assert_eq!(result, efi::Status::INVALID_PARAMETER);
        });
    }

    #[test]
    fn test_create_event_ex_virtual_address_change() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            // EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE should fail with create_event_ex
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event_ex(
                    efi::EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
                    efi::TPL_CALLBACK,
                    Some(test_notify),
                    ptr::null(),
                    ptr::null(),
                    &mut event,
                )
            };

            assert_eq!(result, efi::Status::INVALID_PARAMETER);
        });
    }

    #[test]
    fn test_close_event() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let _ = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };

            let result = EVENT_DB.close_event(event);

            assert!(result.is_ok());
            assert!(!EVENT_DB.is_valid(event));
        });
    }

    #[test]
    fn test_signal_event() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let _ = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };
            let result = signal_event(event);

            assert_eq!(result, efi::Status::SUCCESS);
            assert!(EVENT_DB.read_and_clear_signaled(event).is_ok());
        });
    }

    #[test]
    fn test_wait_for_event_signaled() {
        with_locked_state(|| {
            CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
            let mut event: efi::Event = ptr::null_mut();
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            unsafe {
                create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event)
            };
            signal_event(event);

            let events: [efi::Event; 1] = [event];
            let mut index: usize = 0;

            let mut test_wait = || {
                // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
                let status = unsafe { wait_for_event(1, events.as_ptr() as *mut efi::Event, &mut index as *mut usize) };
                assert_eq!(status, efi::Status::SUCCESS);
                assert_eq!(index, 0);
            };

            test_wait();

            let _ = close_event(event);
        });
    }

    #[test]
    fn test_timer_delay_relative_basic() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            let initial_time = 1000u64;
            SYSTEM_TIME.store(initial_time, Ordering::SeqCst);

            let wait_time = 500u64;
            let result = set_timer(event, 1 /* TimerDelay::Relative */, wait_time);
            assert_eq!(result, efi::Status::SUCCESS);
        })
    }

    #[test]
    fn test_timer_delay_error_handling() {
        with_locked_state(|| {
            // Test with invalid event
            let invalid_event: efi::Event = ptr::null_mut();
            let result = set_timer(invalid_event, 1 /* TimerDelay::Relative */, 100);

            // Should return an error status
            assert_ne!(result, efi::Status::SUCCESS);

            // Test with invalid timer time
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // Create timer event
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Set timer with an invalid timer type
            let invalid_timer_type = 10; // Any value not defined in TimerDelay enum
            let result = set_timer(event, invalid_timer_type, 100);

            // Should return an error status
            assert_ne!(result, efi::Status::SUCCESS);

            let _ = EVENT_DB.close_event(event);
        });
    }

    #[test]
    fn test_set_timer_cancel() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Set a timer
            let result = set_timer(event, 1 /* TimerDelay::Relative */, 500);
            assert_eq!(result, efi::Status::SUCCESS);

            // Cancel the timer
            let result = set_timer(event, 0 /* TimerDelay::Cancel */, 0);
            assert_eq!(result, efi::Status::SUCCESS);

            // Clean up
            let _ = close_event(event);
        });
    }

    #[test]
    fn test_set_timer_periodic() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            let notify_fn: Option<efi::EventNotify> = Some(test_notify);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_TIMER | efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    notify_fn,
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Set periodic timer
            let result = set_timer(event, 2 /* TimerDelay::Periodic */, 100);
            assert_eq!(result, efi::Status::SUCCESS);

            // Clean up
            let _ = close_event(event);
        });
    }

    // Test for event notifications
    #[test]
    fn test_event_notification() {
        with_locked_state(|| {
            // Ensure we start from a low TPL so that signal_event's raise/restore will dispatch notifies
            CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
            NOTIFY_CALLED.store(false, Ordering::SeqCst);

            let mut event: efi::Event = ptr::null_mut();
            // Create notification signal event
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_CALLBACK,
                    Some(tracking_notify),
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Signal the event
            let result = signal_event(event);
            assert_eq!(result, efi::Status::SUCCESS);

            // Check if notification was called
            assert!(NOTIFY_CALLED.load(Ordering::SeqCst));

            // Clean up
            let _ = close_event(event);
        });
    }

    #[test]
    fn test_event_notification_with_tpl_change_fires_lower_events() {
        with_locked_state(|| {
            NOTIFY_CALLED.store(false, Ordering::SeqCst);

            // special callback that does TPL manipulation.
            extern "efiapi" fn test_tpl_switching_notify(_event: efi::Event, _context: *mut c_void) {
                let old_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
                restore_tpl(efi::TPL_APPLICATION);

                if old_tpl > efi::TPL_APPLICATION {
                    raise_tpl(old_tpl);
                }
            }

            let mut event: efi::Event = ptr::null_mut();
            // Create notification signal event
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_CALLBACK,
                    Some(tracking_notify),
                    ptr::null_mut(),
                    &mut event,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            let mut event2: efi::Event = ptr::null_mut();
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(
                    efi::EVT_NOTIFY_SIGNAL,
                    efi::TPL_NOTIFY,
                    Some(test_tpl_switching_notify),
                    ptr::null_mut(),
                    &mut event2,
                )
            };
            assert_eq!(result, efi::Status::SUCCESS);

            //raise TPL to callback than event
            let old_tpl = raise_tpl(efi::TPL_CALLBACK);

            // Signal the event
            let result = signal_event(event);
            assert_eq!(result, efi::Status::SUCCESS);

            // notification should not have been called (because current TPL >= notification TPL).
            assert!(!NOTIFY_CALLED.load(Ordering::SeqCst));

            // Signal the TPL manipulation event. This should fire and lower the TPL so the event1 notification should
            // signal.
            let result = signal_event(event2);
            assert_eq!(result, efi::Status::SUCCESS);

            // notification should have been called (current TPL was briefly lowered to notification TPL).
            assert!(NOTIFY_CALLED.load(Ordering::SeqCst));

            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);

            // Clean up
            let _ = close_event(event);
            let _ = close_event(event2);
            restore_tpl(old_tpl);
        });
    }

    #[test]
    fn test_wait_for_event_null_parameters() {
        with_locked_state(|| {
            let mut index: usize = 0;
            let events: [efi::Event; 1] = [ptr::null_mut()];

            // Test null event array
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let status = unsafe { wait_for_event(1, ptr::null_mut(), &mut index as *mut usize) };
            assert_eq!(status, efi::Status::INVALID_PARAMETER);

            // Test zero events
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let status = unsafe { wait_for_event(0, events.as_ptr() as *mut efi::Event, &mut index as *mut usize) };
            assert_eq!(status, efi::Status::INVALID_PARAMETER);
        });
    }

    #[test]
    fn test_wait_for_event_wrong_tpl() {
        with_locked_state(|| {
            let mut index: usize = 0;
            let events: [efi::Event; 1] = [ptr::null_mut()];

            // Set TPL to something other than APPLICATION
            CURRENT_TPL.store(efi::TPL_NOTIFY, Ordering::SeqCst);

            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let status = unsafe { wait_for_event(1, events.as_ptr() as *mut efi::Event, &mut index as *mut usize) };
            assert_eq!(status, efi::Status::UNSUPPORTED);

            CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);
        });
    }

    // Tests for check_event function
    #[test]
    fn test_check_event_with_invalid_event() {
        with_locked_state(|| {
            let invalid_event: efi::Event = ptr::null_mut();
            let result = check_event(invalid_event);
            assert_ne!(result, efi::Status::SUCCESS);
        });
    }

    #[test]
    fn test_check_event_notify_signal_type() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            // Create a notification signal event
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(efi::EVT_NOTIFY_SIGNAL, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event)
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Check event should fail for notify signal events
            let result = check_event(event);
            assert_eq!(result, efi::Status::INVALID_PARAMETER);

            // Clean up
            let _ = close_event(event);
        });
    }

    #[test]
    fn test_check_event_signaled_event() {
        with_locked_state(|| {
            let mut event: efi::Event = ptr::null_mut();
            // Create a wait event
            // SAFETY: Test code - all pointers are test-controlled and valid for the duration of the call.
            let result = unsafe {
                create_event(efi::EVT_NOTIFY_WAIT, efi::TPL_NOTIFY, Some(test_notify), ptr::null_mut(), &mut event)
            };
            assert_eq!(result, efi::Status::SUCCESS);

            // Signal the event
            let result = signal_event(event);
            assert_eq!(result, efi::Status::SUCCESS);

            // Check event should succeed for signaled events
            let result = check_event(event);
            assert_eq!(result, efi::Status::SUCCESS);

            // Checking again should return NOT_READY as it's been cleared
            let result = check_event(event);
            assert_eq!(result, efi::Status::NOT_READY);

            // Clean up
            let _ = close_event(event);
        });
    }

    // Tests for TPL functions
    #[test]
    fn test_raise_tpl_sequence() {
        with_locked_state(|| {
            // Store original TPL to restore later
            let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);

            // Set known starting TPL
            CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);

            // Test raising from APPLICATION to CALLBACK
            let prev_tpl = raise_tpl(efi::TPL_CALLBACK);
            assert_eq!(prev_tpl, efi::TPL_APPLICATION);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);

            // Test raising from CALLBACK to NOTIFY
            let prev_tpl = raise_tpl(efi::TPL_NOTIFY);
            assert_eq!(prev_tpl, efi::TPL_CALLBACK);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_NOTIFY);

            // Test raising to HIGH_LEVEL (should disable interrupts)
            let prev_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
            assert_eq!(prev_tpl, efi::TPL_NOTIFY);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_HIGH_LEVEL);

            // Restore original TPL
            CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
            // Re-enable interrupts if we left them disabled
            interrupts::enable_interrupts();
        });
    }

    #[test]
    fn test_raise_tpl_too_high() {
        with_locked_state(|| {
            // Instead of calling raise_tpl directly with an invalid value,
            // let's check that the condition that would cause a panic is enforced

            // The function should panic if TPL > HIGH_LEVEL
            let too_high_tpl = efi::TPL_HIGH_LEVEL + 1;

            // We can test the assertion condition without triggering the panic
            let would_panic = too_high_tpl > efi::TPL_HIGH_LEVEL;
            assert!(would_panic, "TPL values greater than HIGH_LEVEL should not be allowed");

            // Additionally, we can test that valid TPL values work correctly
            let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);
            CURRENT_TPL.store(efi::TPL_APPLICATION, Ordering::SeqCst);

            // Test with valid value - should not panic
            let prev_tpl = raise_tpl(efi::TPL_HIGH_LEVEL);
            assert_eq!(prev_tpl, efi::TPL_APPLICATION);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_HIGH_LEVEL);

            // Restore original TPL
            CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
        });
    }

    #[test]
    fn test_raise_tpl_to_lower() {
        with_locked_state(|| {
            // Store original TPL to restore later
            let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);

            // Instead of triggering a panic, we'll test the condition
            // that would cause a panic
            let current_tpl = efi::TPL_NOTIFY;
            let lower_tpl = efi::TPL_CALLBACK; // Lower than NOTIFY

            // Set starting TPL to NOTIFY
            CURRENT_TPL.store(current_tpl, Ordering::SeqCst);

            // This would trigger the panic in raise_tpl:
            // raise_tpl(lower_tpl)

            // Instead, verify the condition that would cause a panic
            let would_panic = lower_tpl < current_tpl;
            assert!(would_panic, "Attempting to raise TPL to a lower value should cause a panic");

            // Test valid case - should not panic
            let prev_tpl = raise_tpl(current_tpl); // Same level, should be fine
            assert_eq!(prev_tpl, current_tpl);

            let higher_tpl = efi::TPL_HIGH_LEVEL; // Higher than NOTIFY
            let prev_tpl = raise_tpl(higher_tpl);
            assert_eq!(prev_tpl, current_tpl);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), higher_tpl);

            // Restore original TPL
            CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
        });
    }

    #[test]
    fn test_restore_tpl_sequence() {
        with_locked_state(|| {
            // Store original TPL to restore later
            let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);

            // Set known starting TPL
            CURRENT_TPL.store(efi::TPL_HIGH_LEVEL, Ordering::SeqCst);
            interrupts::disable_interrupts();

            // Test restoring from HIGH_LEVEL to NOTIFY
            restore_tpl(efi::TPL_NOTIFY);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_NOTIFY);

            // Test restoring from NOTIFY to CALLBACK
            restore_tpl(efi::TPL_CALLBACK);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_CALLBACK);

            // Test restoring from CALLBACK to APPLICATION
            restore_tpl(efi::TPL_APPLICATION);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), efi::TPL_APPLICATION);

            // Restore original TPL
            CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
        });
    }

    #[test]
    fn test_restore_tpl_to_higher() {
        with_locked_state(|| {
            // Store original TPL to restore later
            let original_tpl = CURRENT_TPL.load(Ordering::SeqCst);

            // Set starting TPL to a known value
            let current_tpl = efi::TPL_NOTIFY;
            let higher_tpl = efi::TPL_HIGH_LEVEL; // Higher than NOTIFY

            // Set starting TPL
            CURRENT_TPL.store(current_tpl, Ordering::SeqCst);

            // This would trigger the panic in restore_tpl:
            // restore_tpl(higher_tpl)

            // Instead, verify the condition that would cause a panic
            let would_panic = higher_tpl > current_tpl;
            assert!(would_panic, "Attempting to restore TPL to a higher value should cause a panic");

            // Test valid case - should not panic
            restore_tpl(current_tpl); // Same level, should be fine
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), current_tpl);

            let lower_tpl = efi::TPL_CALLBACK; // Lower than NOTIFY
            restore_tpl(lower_tpl);
            assert_eq!(CURRENT_TPL.load(Ordering::SeqCst), lower_tpl);

            // Restore original TPL
            CURRENT_TPL.store(original_tpl, Ordering::SeqCst);
        });
    }

    // Tests for GCD and initialization functions
    #[test]
    fn test_gcd_map_change() {
        with_locked_state(|| {
            // Test each map change type
            gcd_map_change(gcd::MapChangeType::AddMemorySpace);
            gcd_map_change(gcd::MapChangeType::AllocateMemorySpace);
            gcd_map_change(gcd::MapChangeType::FreeMemorySpace);
            gcd_map_change(gcd::MapChangeType::RemoveMemorySpace);
            gcd_map_change(gcd::MapChangeType::SetMemoryAttributes);
            gcd_map_change(gcd::MapChangeType::SetMemoryCapabilities);
        });
    }

    #[test]
    fn test_timer_tick() {
        with_locked_state(|| {
            let original_time = SYSTEM_TIME.load(Ordering::SeqCst);

            let test_time = 1000;
            timer_tick(test_time);

            assert_eq!(SYSTEM_TIME.load(Ordering::SeqCst), original_time + test_time);

            SYSTEM_TIME.store(original_time, Ordering::SeqCst);
        });
    }

    // Mock for init_events_support test
    #[test]
    fn test_init_events_support() {
        with_locked_state(|| {
            let mut st = EfiSystemTable::allocate_new_table();

            // Initialize events support
            init_events_support(&mut st);

            // Verify function pointers are updated
            let boot_services = st.boot_services().get();
            assert_eq!(boot_services.create_event as usize, create_event as *const () as usize);
            assert_eq!(boot_services.create_event_ex as usize, create_event_ex as *const () as usize);
            assert_eq!(boot_services.close_event as usize, close_event as *const () as usize);
            assert_eq!(boot_services.signal_event as usize, signal_event as *const () as usize);
            assert_eq!(boot_services.wait_for_event as usize, wait_for_event as *const () as usize);
            assert_eq!(boot_services.check_event as usize, check_event as *const () as usize);
            assert_eq!(boot_services.set_timer as usize, set_timer as *const () as usize);
            assert_eq!(boot_services.raise_tpl as usize, raise_tpl as *const () as usize);
            assert_eq!(boot_services.restore_tpl as usize, restore_tpl as *const () as usize);
        });
    }
}
