//! Auto-detect the best available compute backend.
//!
//! Probes backends in order of performance: CUDA > WGPU > CPU.
//! Falls through gracefully — the CPU backend is always available.

use crate::compute::cpu::CpuBackend;
use crate::compute::ComputeBackend;

/// Detect and return the best available compute backend.
///
/// Selection priority:
/// 1. CUDA (NVIDIA GPUs) — requires `cuda` feature
/// 2. WGPU (ROCm, Metal, Vulkan) — requires `wgpu` feature
/// 3. CPU — always available, zero dependencies
pub fn detect_best_backend() -> Box<dyn ComputeBackend> {
    // CUDA backend — probe for NVIDIA GPU
    #[cfg(feature = "cuda")]
    {
        if let Some(cuda) = super::cuda::CudaBackend::try_new() {
            tracing::info!("Using CUDA backend: {}", cuda.name());
            return Box::new(cuda);
        }
        tracing::debug!("CUDA feature enabled but no NVIDIA GPU detected, falling through");
    }

    // WGPU backend — probe for any WGPU adapter (Metal, Vulkan, DX12, ROCm)
    #[cfg(feature = "wgpu")]
    {
        if let Some(wgpu) = super::wgpu_backend::WgpuBackend::try_new() {
            tracing::info!("Using WGPU backend ({})", wgpu.adapter_name());
            return Box::new(wgpu);
        }
        tracing::debug!("WGPU feature enabled but no adapter detected, falling through");
    }

    tracing::info!("Using CPU compute backend");
    Box::new(CpuBackend::new())
}

/// List all compute backends available at compile time.
///
/// Returns short identifiers: "cpu", "cuda", "wgpu".
/// Does NOT probe hardware — just reports what was compiled in.
pub fn available_backends() -> Vec<&'static str> {
    #[allow(unused_mut)] // mut needed when cuda/wgpu features add entries
    let mut backends = vec!["cpu"];

    #[cfg(feature = "cuda")]
    backends.push("cuda");

    #[cfg(feature = "wgpu")]
    backends.push("wgpu");

    backends
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect_best_returns_cpu_without_features() {
        let backend = detect_best_backend();
        // Without cuda/wgpu features, must fall back to CPU
        assert_eq!(backend.name(), "cpu");
    }

    #[test]
    fn test_available_backends_always_includes_cpu() {
        let backends = available_backends();
        assert!(backends.contains(&"cpu"));
    }

    #[test]
    fn test_available_backends_count() {
        let backends = available_backends();
        // Verify count matches enabled features
        let expected = 1  // CPU always present
            + cfg!(feature = "cuda") as usize
            + cfg!(feature = "wgpu") as usize;
        assert_eq!(backends.len(), expected);
    }

    #[test]
    fn test_detect_returns_send_sync() {
        let backend = detect_best_backend();
        // Verify the trait object is Send + Sync (required for multi-threaded inference)
        fn assert_send_sync<T: Send + Sync>(_: &T) {}
        assert_send_sync(&backend);
    }
}
