#[cfg(target_os = "linux")]
mod memory {
    use std::sync::atomic::{AtomicBool, Ordering};

    static MLOCKALL_CALLED: AtomicBool = AtomicBool::new(false);
    static MLOCKALL_SUCCESS: AtomicBool = AtomicBool::new(false);

    /// Locks all current and future memory pages to prevent page faults during RT execution.
    /// Must be called BEFORE spawning any threads for maximum effectiveness.
    pub fn lock_memory() -> bool {
        if MLOCKALL_CALLED.swap(true, Ordering::SeqCst) {
            return MLOCKALL_SUCCESS.load(Ordering::SeqCst);
        }

        let result = unsafe { libc::mlockall(libc::MCL_CURRENT | libc::MCL_FUTURE) };

        if result == 0 {
            MLOCKALL_SUCCESS.store(true, Ordering::SeqCst);
            true
        } else {
            let errno = std::io::Error::last_os_error();
            eprintln!("[cagire] mlockall failed: {errno}");
            eprintln!("[cagire] Memory locking disabled. For best RT performance on Linux:");
            eprintln!("[cagire]   1. Add user to 'audio' group: sudo usermod -aG audio $USER");
            eprintln!("[cagire]   2. Add to /etc/security/limits.conf:");
            eprintln!("[cagire]      @audio   -  memlock    unlimited");
            eprintln!("[cagire]   3. Log out and back in");
            false
        }
    }
}

#[cfg(target_os = "linux")]
pub use memory::lock_memory;

#[cfg(not(target_os = "linux"))]
pub fn lock_memory() -> bool {
    true
}

/// Attempts to set realtime scheduling priority for the current thread.
/// Returns true if RT priority was successfully set, false otherwise.
#[cfg(target_os = "macos")]
pub fn set_realtime_priority() -> bool {
    // macOS: use THREAD_TIME_CONSTRAINT_POLICY for true RT scheduling.
    // This is the same mechanism CoreAudio uses for its audio threads.
    // SCHED_FIFO/RR require root on macOS, but time constraint policy does not.
    unsafe {
        let thread = libc::pthread_self();

        #[repr(C)]
        struct ThreadTimeConstraintPolicy {
            period: u32,
            computation: u32,
            constraint: u32,
            preemptible: i32,
        }

        const THREAD_TIME_CONSTRAINT_POLICY_ID: u32 = 2;
        const THREAD_TIME_CONSTRAINT_POLICY_COUNT: u32 = 4;

        // ~1ms period at ~1GHz mach_absolute_time ticks (typical for audio)
        let policy = ThreadTimeConstraintPolicy {
            period: 1_000_000,
            computation: 500_000,
            constraint: 1_000_000,
            preemptible: 1,
        };

        extern "C" {
            fn thread_policy_set(
                thread: libc::pthread_t,
                flavor: u32,
                policy_info: *const ThreadTimeConstraintPolicy,
                count: u32,
            ) -> i32;
        }

        let result = thread_policy_set(
            thread,
            THREAD_TIME_CONSTRAINT_POLICY_ID,
            &policy,
            THREAD_TIME_CONSTRAINT_POLICY_COUNT,
        );
        result == 0
    }
}

/// Attempts to set realtime scheduling priority for the current thread.
/// Returns true if RT priority was successfully set, false otherwise.
///
/// On Linux, this requires either:
/// - CAP_SYS_NICE capability, or
/// - Configured rtprio limits in /etc/security/limits.conf:
///   @audio   -  rtprio     95
///   @audio   -  memlock    unlimited
#[cfg(all(target_os = "linux", feature = "cli"))]
pub fn set_realtime_priority() -> bool {
    use thread_priority::unix::{
        set_thread_priority_and_policy, thread_native_id, NormalThreadSchedulePolicy,
        RealtimeThreadSchedulePolicy, ThreadSchedulePolicy,
    };
    use thread_priority::ThreadPriority;

    let tid = thread_native_id();

    let fifo = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo);
    if set_thread_priority_and_policy(tid, ThreadPriority::Max, fifo).is_ok() {
        return true;
    }

    let rr = ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::RoundRobin);
    if set_thread_priority_and_policy(tid, ThreadPriority::Max, rr).is_ok() {
        return true;
    }

    let _ = set_thread_priority_and_policy(
        tid,
        ThreadPriority::Max,
        ThreadSchedulePolicy::Normal(NormalThreadSchedulePolicy::Other),
    );

    unsafe {
        libc::setpriority(libc::PRIO_PROCESS, 0, -20);
    }

    false
}

#[cfg(all(target_os = "linux", not(feature = "cli")))]
pub fn set_realtime_priority() -> bool {
    false
}

#[cfg(not(any(unix, target_os = "windows")))]
pub fn set_realtime_priority() -> bool {
    false
}

#[cfg(all(target_os = "windows", feature = "cli"))]
pub fn set_realtime_priority() -> bool {
    use thread_priority::{set_current_thread_priority, ThreadPriority};
    set_current_thread_priority(ThreadPriority::Max).is_ok()
}

#[cfg(all(target_os = "windows", not(feature = "cli")))]
pub fn set_realtime_priority() -> bool {
    false
}

/// High-precision sleep using clock_nanosleep on Linux.
/// Uses monotonic clock for jitter-free sleeping.
#[cfg(target_os = "linux")]
pub fn precise_sleep_us(micros: u64) {
    let duration_ns = micros * 1000;
    let ts = libc::timespec {
        tv_sec: (duration_ns / 1_000_000_000) as i64,
        tv_nsec: (duration_ns % 1_000_000_000) as i64,
    };
    unsafe {
        libc::clock_nanosleep(libc::CLOCK_MONOTONIC, 0, &ts, std::ptr::null_mut());
    }
}

#[cfg(not(target_os = "linux"))]
pub fn precise_sleep_us(micros: u64) {
    std::thread::sleep(std::time::Duration::from_micros(micros));
}
