//! Windows-specific memory optimization implementations

use crate::core::error::PlottingError;
use std::mem;
use std::ptr;

// Windows API types and constants
type DWORD = u32;
type DWORDLONG = u64;
type HANDLE = *mut std::ffi::c_void;
type BOOL = i32;

const TRUE: BOOL = 1;
const FALSE: BOOL = 0;

#[repr(C)]
struct MEMORYSTATUSEX {
    dwLength: DWORD,
    dwMemoryLoad: DWORD,
    ullTotalPhys: DWORDLONG,
    ullAvailPhys: DWORDLONG,
    ullTotalPageFile: DWORDLONG,
    ullAvailPageFile: DWORDLONG,
    ullTotalVirtual: DWORDLONG,
    ullAvailVirtual: DWORDLONG,
    ullAvailExtendedVirtual: DWORDLONG,
}

#[repr(C)]
struct SYSTEM_INFO {
    wProcessorArchitecture: u16,
    wReserved: u16,
    dwPageSize: DWORD,
    lpMinimumApplicationAddress: *mut std::ffi::c_void,
    lpMaximumApplicationAddress: *mut std::ffi::c_void,
    dwActiveProcessorMask: usize,
    dwNumberOfProcessors: DWORD,
    dwProcessorType: DWORD,
    dwAllocationGranularity: DWORD,
    wProcessorLevel: u16,
    wProcessorRevision: u16,
}

#[repr(C)]
struct PERFORMANCE_INFORMATION {
    cb: DWORD,
    CommitTotal: usize,
    CommitLimit: usize,
    CommitPeak: usize,
    PhysicalTotal: usize,
    PhysicalAvailable: usize,
    SystemCache: usize,
    KernelTotal: usize,
    KernelPaged: usize,
    KernelNonpaged: usize,
    PageSize: usize,
    HandleCount: DWORD,
    ProcessCount: DWORD,
    ThreadCount: DWORD,
}

// SAFETY: These are FFI declarations for stable Win32 APIs. Callers validate
// all pointers, sizes, and return codes at each call site.
unsafe extern "system" {
    fn GlobalMemoryStatusEx(lpBuffer: *mut MEMORYSTATUSEX) -> BOOL;
    fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO) -> ();
    fn GetPerformanceInfo(pPerformanceInformation: *mut PERFORMANCE_INFORMATION, cb: DWORD)
    -> BOOL;
    fn GetCurrentProcess() -> HANDLE;
    fn SetProcessWorkingSetSize(
        hProcess: HANDLE,
        dwMinimumWorkingSetSize: usize,
        dwMaximumWorkingSetSize: usize,
    ) -> BOOL;
    fn GetLogicalProcessorInformation(
        Buffer: *mut std::ffi::c_void,
        ReturnedLength: *mut DWORD,
    ) -> BOOL;
    fn GetLargePageMinimum() -> usize;
    fn VirtualAlloc(
        lpAddress: *mut std::ffi::c_void,
        dwSize: usize,
        flAllocationType: DWORD,
        flProtect: DWORD,
    ) -> *mut std::ffi::c_void;
    fn VirtualFree(lpAddress: *mut std::ffi::c_void, dwSize: usize, dwFreeType: DWORD) -> BOOL;
}

// Memory allocation constants
const MEM_COMMIT: DWORD = 0x1000;
const MEM_RESERVE: DWORD = 0x2000;
const MEM_LARGE_PAGES: DWORD = 0x20000000;
const MEM_RELEASE: DWORD = 0x8000;
const PAGE_READWRITE: DWORD = 0x04;

/// Get total system memory on Windows
pub fn get_total_memory() -> Result<u64, PlottingError> {
    // SAFETY: `memory_status` is a valid initialized struct with correct
    // `dwLength`; Windows API writes into provided mutable pointer.
    unsafe {
        let mut memory_status: MEMORYSTATUSEX = mem::zeroed();
        memory_status.dwLength = mem::size_of::<MEMORYSTATUSEX>() as DWORD;

        let result = GlobalMemoryStatusEx(&mut memory_status);
        if result != FALSE {
            Ok(memory_status.ullTotalPhys)
        } else {
            Err(PlottingError::SystemError(
                "Failed to get total memory".to_string(),
            ))
        }
    }
}

/// Get available system memory on Windows
pub fn get_available_memory() -> Result<u64, PlottingError> {
    // SAFETY: same contract as `get_total_memory`; output buffer and size are valid.
    unsafe {
        let mut memory_status: MEMORYSTATUSEX = mem::zeroed();
        memory_status.dwLength = mem::size_of::<MEMORYSTATUSEX>() as DWORD;

        let result = GlobalMemoryStatusEx(&mut memory_status);
        if result != FALSE {
            Ok(memory_status.ullAvailPhys)
        } else {
            Err(PlottingError::SystemError(
                "Failed to get available memory".to_string(),
            ))
        }
    }
}

/// Get detailed memory status on Windows
pub fn get_memory_status() -> Result<WindowsMemoryStatus, PlottingError> {
    // SAFETY: same contract as `get_total_memory`; output buffer and size are valid.
    unsafe {
        let mut memory_status: MEMORYSTATUSEX = mem::zeroed();
        memory_status.dwLength = mem::size_of::<MEMORYSTATUSEX>() as DWORD;

        let result = GlobalMemoryStatusEx(&mut memory_status);
        if result != FALSE {
            Ok(WindowsMemoryStatus {
                memory_load: memory_status.dwMemoryLoad,
                total_physical: memory_status.ullTotalPhys,
                available_physical: memory_status.ullAvailPhys,
                total_page_file: memory_status.ullTotalPageFile,
                available_page_file: memory_status.ullAvailPageFile,
                total_virtual: memory_status.ullTotalVirtual,
                available_virtual: memory_status.ullAvailVirtual,
                available_extended_virtual: memory_status.ullAvailExtendedVirtual,
            })
        } else {
            Err(PlottingError::SystemError(
                "Failed to get memory status".to_string(),
            ))
        }
    }
}

/// Get performance information on Windows
pub fn get_performance_info() -> Result<WindowsPerformanceInfo, PlottingError> {
    // SAFETY: `perf_info` is properly sized and mutable for `GetPerformanceInfo`.
    unsafe {
        let mut perf_info: PERFORMANCE_INFORMATION = mem::zeroed();
        perf_info.cb = mem::size_of::<PERFORMANCE_INFORMATION>() as DWORD;

        let result = GetPerformanceInfo(&mut perf_info, perf_info.cb);
        if result != FALSE {
            Ok(WindowsPerformanceInfo {
                commit_total: perf_info.CommitTotal * perf_info.PageSize,
                commit_limit: perf_info.CommitLimit * perf_info.PageSize,
                commit_peak: perf_info.CommitPeak * perf_info.PageSize,
                physical_total: perf_info.PhysicalTotal * perf_info.PageSize,
                physical_available: perf_info.PhysicalAvailable * perf_info.PageSize,
                system_cache: perf_info.SystemCache * perf_info.PageSize,
                kernel_total: perf_info.KernelTotal * perf_info.PageSize,
                kernel_paged: perf_info.KernelPaged * perf_info.PageSize,
                kernel_nonpaged: perf_info.KernelNonpaged * perf_info.PageSize,
                page_size: perf_info.PageSize,
                handle_count: perf_info.HandleCount,
                process_count: perf_info.ProcessCount,
                thread_count: perf_info.ThreadCount,
            })
        } else {
            Err(PlottingError::SystemError(
                "Failed to get performance info".to_string(),
            ))
        }
    }
}

/// Get system information on Windows
pub fn get_system_info() -> Result<WindowsSystemInfo, PlottingError> {
    // SAFETY: `GetSystemInfo` expects a valid mutable pointer to `SYSTEM_INFO`.
    unsafe {
        let mut sys_info: SYSTEM_INFO = mem::zeroed();
        GetSystemInfo(&mut sys_info);

        Ok(WindowsSystemInfo {
            processor_architecture: sys_info.wProcessorArchitecture,
            page_size: sys_info.dwPageSize as usize,
            minimum_application_address: sys_info.lpMinimumApplicationAddress as usize,
            maximum_application_address: sys_info.lpMaximumApplicationAddress as usize,
            active_processor_mask: sys_info.dwActiveProcessorMask,
            number_of_processors: sys_info.dwNumberOfProcessors as usize,
            processor_type: sys_info.dwProcessorType,
            allocation_granularity: sys_info.dwAllocationGranularity as usize,
            processor_level: sys_info.wProcessorLevel,
            processor_revision: sys_info.wProcessorRevision,
        })
    }
}

/// Check if huge pages are supported on Windows
pub fn check_hugepage_support() -> bool {
    check_large_page_support()
}

/// Check if large pages are supported on Windows
pub fn check_large_page_support() -> bool {
    // SAFETY: `GetLargePageMinimum` has no preconditions.
    unsafe {
        let min_large_page = GetLargePageMinimum();
        min_large_page > 0
    }
}

/// Get minimum large page size on Windows
pub fn get_large_page_minimum() -> usize {
    // SAFETY: `GetLargePageMinimum` has no preconditions.
    unsafe { GetLargePageMinimum() }
}

/// Configure working set size for the current process
pub fn configure_working_set(
    min_size: Option<usize>,
    max_size: Option<usize>,
) -> Result<(), PlottingError> {
    // SAFETY: `GetCurrentProcess` returns a pseudo handle valid for this process,
    // and the working set sizes are passed directly as documented values.
    unsafe {
        let process = GetCurrentProcess();
        let min_ws = min_size.unwrap_or(0);
        let max_ws = max_size.unwrap_or(0);

        let result = SetProcessWorkingSetSize(process, min_ws, max_ws);
        if result != FALSE {
            Ok(())
        } else {
            Err(PlottingError::SystemError(
                "Failed to configure working set size".to_string(),
            ))
        }
    }
}

/// Get NUMA node count on Windows
pub fn get_numa_nodes() -> usize {
    // This is a simplified implementation
    // A full implementation would use GetLogicalProcessorInformationEx
    // SAFETY: first call passes null buffer to query required size; second call
    // uses a valid allocated buffer and checks API result.
    unsafe {
        let mut buffer_length: DWORD = 0;

        // First call to get required buffer size
        GetLogicalProcessorInformation(ptr::null_mut(), &mut buffer_length);

        if buffer_length == 0 {
            return 1; // Default to single NUMA node
        }

        let mut buffer = vec![0u8; buffer_length as usize];
        let result = GetLogicalProcessorInformation(
            buffer.as_mut_ptr() as *mut std::ffi::c_void,
            &mut buffer_length,
        );

        if result != FALSE {
            // Parse the processor information to count NUMA nodes
            // This is simplified - in practice you'd parse SYSTEM_LOGICAL_PROCESSOR_INFORMATION
            // structures to count NumaNode relationships
            let estimated_nodes = (buffer_length as usize / 48).max(1); // Rough estimate
            estimated_nodes.min(64) // Cap at reasonable maximum
        } else {
            1 // Default single NUMA node
        }
    }
}

/// Allocate large page memory on Windows
pub fn allocate_large_pages(size: usize) -> Result<*mut std::ffi::c_void, PlottingError> {
    if !check_large_page_support() {
        return Err(PlottingError::SystemError(
            "Large pages not supported".to_string(),
        ));
    }

    // SAFETY: `VirtualAlloc` is called with documented flags; null result is handled.
    unsafe {
        let ptr = VirtualAlloc(
            ptr::null_mut(),
            size,
            MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES,
            PAGE_READWRITE,
        );

        if ptr.is_null() {
            Err(PlottingError::SystemError(
                "Failed to allocate large pages".to_string(),
            ))
        } else {
            Ok(ptr)
        }
    }
}

/// Free large page memory on Windows
pub fn free_large_pages(ptr: *mut std::ffi::c_void) -> Result<(), PlottingError> {
    // SAFETY: caller provides a pointer returned by `VirtualAlloc`; `VirtualFree`
    // return value is checked.
    unsafe {
        let result = VirtualFree(ptr, 0, MEM_RELEASE);
        if result != FALSE {
            Ok(())
        } else {
            Err(PlottingError::SystemError(
                "Failed to free large pages".to_string(),
            ))
        }
    }
}

/// Get memory pressure level on Windows
pub fn get_memory_pressure() -> Result<MemoryPressureLevel, PlottingError> {
    let memory_status = get_memory_status()?;
    let perf_info = get_performance_info()?;

    // Calculate memory pressure based on multiple factors
    let physical_pressure = memory_status.memory_load as f64 / 100.0;
    let commit_pressure = perf_info.commit_total as f64 / perf_info.commit_limit as f64;

    let overall_pressure = physical_pressure.max(commit_pressure);

    let pressure_level = if overall_pressure > 0.95 {
        MemoryPressureLevel::Critical
    } else if overall_pressure > 0.85 {
        MemoryPressureLevel::High
    } else if overall_pressure > 0.70 {
        MemoryPressureLevel::Medium
    } else {
        MemoryPressureLevel::Low
    };

    Ok(pressure_level)
}

#[derive(Debug, Clone)]
pub struct WindowsMemoryStatus {
    pub memory_load: u32,                // Percent of memory in use
    pub total_physical: u64,             // Total physical memory
    pub available_physical: u64,         // Available physical memory
    pub total_page_file: u64,            // Total page file size
    pub available_page_file: u64,        // Available page file
    pub total_virtual: u64,              // Total virtual address space
    pub available_virtual: u64,          // Available virtual address space
    pub available_extended_virtual: u64, // Available extended virtual
}

#[derive(Debug, Clone)]
pub struct WindowsPerformanceInfo {
    pub commit_total: usize,       // Current committed memory
    pub commit_limit: usize,       // Maximum committed memory
    pub commit_peak: usize,        // Peak committed memory
    pub physical_total: usize,     // Total physical memory
    pub physical_available: usize, // Available physical memory
    pub system_cache: usize,       // System cache memory
    pub kernel_total: usize,       // Total kernel memory
    pub kernel_paged: usize,       // Paged kernel memory
    pub kernel_nonpaged: usize,    // Non-paged kernel memory
    pub page_size: usize,          // System page size
    pub handle_count: u32,         // System handle count
    pub process_count: u32,        // Process count
    pub thread_count: u32,         // Thread count
}

#[derive(Debug, Clone)]
pub struct WindowsSystemInfo {
    pub processor_architecture: u16,
    pub page_size: usize,
    pub minimum_application_address: usize,
    pub maximum_application_address: usize,
    pub active_processor_mask: usize,
    pub number_of_processors: usize,
    pub processor_type: u32,
    pub allocation_granularity: usize,
    pub processor_level: u16,
    pub processor_revision: u16,
}

#[derive(Debug, Clone)]
pub enum MemoryPressureLevel {
    Low,
    Medium,
    High,
    Critical,
}

/// Windows-specific memory optimization recommendations
pub fn get_windows_optimization_recommendations(
    memory_status: &WindowsMemoryStatus,
    perf_info: &WindowsPerformanceInfo,
) -> Vec<String> {
    let mut recommendations = Vec::new();

    // High memory usage
    if memory_status.memory_load > 85 {
        recommendations
            .push("High memory usage detected. Consider reducing memory consumption.".to_string());
    }

    // Page file pressure
    let page_file_usage = (perf_info.commit_total as f64) / (perf_info.commit_limit as f64);
    if page_file_usage > 0.8 {
        recommendations.push(
            "High page file usage. Consider increasing page file size or adding more RAM."
                .to_string(),
        );
    }

    // Fragmentation indicators
    let virtual_fragmentation = (memory_status.total_virtual - memory_status.available_virtual)
        as f64
        / memory_status.total_virtual as f64;
    if virtual_fragmentation > 0.7 {
        recommendations.push(
            "Virtual address space fragmentation detected. Consider process restart.".to_string(),
        );
    }

    // Large page recommendations
    if check_large_page_support() && memory_status.total_physical > 8 * 1024 * 1024 * 1024 {
        recommendations.push(
            "Large pages supported. Consider using large pages for large allocations.".to_string(),
        );
    }

    // Working set optimization
    if perf_info.physical_available < perf_info.physical_total / 10 {
        recommendations.push(
            "Low available physical memory. Consider optimizing working set size.".to_string(),
        );
    }

    recommendations
}

/// Configure low fragmentation heap for better small allocation performance
pub fn enable_low_fragmentation_heap() -> Result<(), PlottingError> {
    // This would typically be done through HeapSetInformation
    // For now, return success as it's usually enabled by default on modern Windows
    Ok(())
}

/// Get heap fragmentation information
pub fn get_heap_fragmentation_info() -> Result<HeapFragmentationInfo, PlottingError> {
    // This is a placeholder implementation
    // A full implementation would use HeapWalk and related APIs
    Ok(HeapFragmentationInfo {
        total_heap_size: 0,
        committed_size: 0,
        uncommitted_size: 0,
        fragmentation_percentage: 0.0,
        largest_free_block: 0,
    })
}

/// Check if memory mapping is supported on Windows
pub fn check_memory_mapping_support() -> Result<bool, PlottingError> {
    // On Windows, memory mapping is always supported
    Ok(true)
}

#[derive(Debug, Clone)]
pub struct HeapFragmentationInfo {
    pub total_heap_size: usize,
    pub committed_size: usize,
    pub uncommitted_size: usize,
    pub fragmentation_percentage: f64,
    pub largest_free_block: usize,
}
