//! Anti-Debugging Detection Module
//!
//! Detects if the process is being traced/debugged
//! and takes evasive action without obvious crashes.

/// Check if any debugger is attached
#[inline(never)]
pub fn is_debugger_present() -> bool {
    #[cfg(windows)]
    {
        windows_debugger_check()
    }
    
    #[cfg(unix)]
    {
        unix_debugger_check()
    }
    
    #[cfg(not(any(windows, unix)))]
    {
        false
    }
}

/// Multiple Windows anti-debug techniques
#[cfg(windows)]
fn windows_debugger_check() -> bool {
    unsafe {
        // Method 1: IsDebuggerPresent API
        if winapi::um::debugapi::IsDebuggerPresent() != 0 {
            return true;
        }
        
        // Method 2: CheckRemoteDebuggerPresent
        let mut is_debugged: i32 = 0;
        let process = winapi::um::processthreadsapi::GetCurrentProcess();
        if winapi::um::debugapi::CheckRemoteDebuggerPresent(process, &mut is_debugged) != 0 {
            if is_debugged != 0 {
                return true;
            }
        }
        
        // Method 3: NtGlobalFlag check (PEB)
        // The NtGlobalFlag in PEB is set to 0x70 when debugged
        if check_peb_being_debugged() {
            return true;
        }
        
        // Method 4: Timing check - debugger breakpoints cause delays
        if timing_check() {
            return true;
        }
    }
    
    false
}

#[cfg(windows)]
unsafe fn check_peb_being_debugged() -> bool {
    // Access PEB through TEB
    // This is a low-level check that many debuggers don't hide
    #[cfg(target_arch = "x86_64")]
    {
        let peb: *const u8;
        unsafe {
            std::arch::asm!(
                "mov {}, gs:[0x60]",
                out(reg) peb,
                options(nostack, nomem)
            );
        }
        
        if !peb.is_null() {
            // BeingDebugged flag at offset 0x2
            let being_debugged = unsafe { *peb.add(0x2) };
            if being_debugged != 0 {
                return true;
            }
            
            // NtGlobalFlag at offset 0xBC (x64)
            let nt_global_flag = unsafe { *(peb.add(0xBC) as *const u32) };
            // FLG_HEAP_ENABLE_TAIL_CHECK | FLG_HEAP_ENABLE_FREE_CHECK | FLG_HEAP_VALIDATE_PARAMETERS
            if nt_global_flag & 0x70 != 0 {
                return true;
            }
        }
    }
    
    false
}

#[cfg(windows)]
fn timing_check() -> bool {
    use std::time::Instant;
    
    // Simple operation that should be instant
    let start = Instant::now();
    
    // Do some trivial work
    let mut x: u64 = 0;
    for i in 0..1000 {
        x = x.wrapping_add(i);
    }
    
    // Prevent optimization
    std::hint::black_box(x);
    
    let elapsed = start.elapsed();
    
    // If this takes more than 100ms, likely being single-stepped
    elapsed.as_millis() > 100
}

/// Linux/Unix anti-debug techniques
#[cfg(unix)]
fn unix_debugger_check() -> bool {
    // Method 1: ptrace self-attach trick
    if ptrace_check() {
        return true;
    }
    
    // Method 2: Check /proc/self/status for TracerPid
    if proc_status_check() {
        return true;
    }
    
    // Method 3: Check parent process name
    if parent_process_check() {
        return true;
    }
    
    // Method 4: Timing check
    if timing_check_unix() {
        return true;
    }
    
    false
}

#[cfg(unix)]
fn ptrace_check() -> bool {
    // PTRACE_TRACEME = 0
    // If we're already being traced, this will fail
    unsafe {
        let result = libc::ptrace(libc::PTRACE_TRACEME, 0, 0, 0);
        if result == -1 {
            // Already being traced
            return true;
        }
        // Detach from ourselves
        libc::ptrace(libc::PTRACE_DETACH, 0, 0, 0);
    }
    false
}

#[cfg(unix)]
fn proc_status_check() -> bool {
    // Read /proc/self/status and check TracerPid
    if let Ok(status) = std::fs::read_to_string("/proc/self/status") {
        for line in status.lines() {
            if line.starts_with("TracerPid:") {
                if let Some(pid_str) = line.split_whitespace().nth(1) {
                    if let Ok(pid) = pid_str.parse::<i32>() {
                        if pid != 0 {
                            return true;
                        }
                    }
                }
            }
        }
    }
    false
}

#[cfg(unix)]
fn parent_process_check() -> bool {
    // Check if parent is a known debugger
    let debuggers = ["gdb", "lldb", "strace", "ltrace", "radare2", "r2", "ida", "x64dbg", "ollydbg"];
    
    if let Ok(ppid_str) = std::fs::read_to_string("/proc/self/stat") {
        let parts: Vec<&str> = ppid_str.split_whitespace().collect();
        if parts.len() > 3 {
            if let Ok(ppid) = parts[3].parse::<i32>() {
                let parent_exe = format!("/proc/{}/exe", ppid);
                if let Ok(path) = std::fs::read_link(&parent_exe) {
                    let name = path.file_name()
                        .and_then(|n| n.to_str())
                        .unwrap_or("");
                    
                    for debugger in &debuggers {
                        if name.contains(debugger) {
                            return true;
                        }
                    }
                }
            }
        }
    }
    false
}

#[cfg(unix)]
fn timing_check_unix() -> bool {
    use std::time::Instant;
    
    let start = Instant::now();
    let mut x: u64 = 0;
    for i in 0..1000 {
        x = x.wrapping_add(i);
    }
    std::hint::black_box(x);
    
    start.elapsed().as_millis() > 100
}

/// Continuous background monitor (call from separate thread)
pub fn start_background_monitor() -> std::thread::JoinHandle<()> {
    std::thread::spawn(|| {
        loop {
            std::thread::sleep(std::time::Duration::from_secs(5));
            
            if is_debugger_present() {
                // Enter decoy mode silently
                crate::decoy_loop();
            }
        }
    })
}
