//! Network device enumeration and control.
//!
//! Provides functions for listing network devices, checking Wi-Fi state,
//! and enabling/disabling Wi-Fi. Uses D-Bus signals for efficient state
//! monitoring instead of polling.

use log::debug;
use zbus::Connection;

use crate::api::models::{ConnectionError, Device, DeviceIdentity, DeviceState};
use crate::core::state_wait::wait_for_wifi_device_ready;
use crate::dbus::{NMDeviceProxy, NMProxy};
use crate::types::constants::device_type;
use crate::Result;

/// Lists all network devices managed by NetworkManager.
///
/// Returns information about each device including its interface name,
/// type (Ethernet, Wi-Fi, etc.), current state, and driver.
pub(crate) async fn list_devices(conn: &Connection) -> Result<Vec<Device>> {
    let proxy = NMProxy::new(conn).await?;
    let paths = proxy.get_devices().await?;

    let mut devices = Vec::new();
    for p in paths {
        let d_proxy = NMDeviceProxy::builder(conn)
            .path(p.clone())?
            .build()
            .await?;

        let interface = d_proxy.interface().await?;
        let raw_type = d_proxy.device_type().await?;
        let current_mac = d_proxy
            .hw_address()
            .await
            .unwrap_or_else(|_| String::from("00:00:00:00:00:00"));
        // PermHwAddress may not be available on all systems/devices
        // If not available, fall back to HwAddress
        let perm_mac = d_proxy
            .perm_hw_address()
            .await
            .unwrap_or_else(|_| current_mac.clone());
        let device_type = raw_type.into();
        let raw_state = d_proxy.state().await?;
        let state = raw_state.into();
        let managed = d_proxy.managed().await.ok();
        let driver = d_proxy.driver().await.ok();

        devices.push(Device {
            path: p.to_string(),
            interface,
            identity: DeviceIdentity {
                permanent_mac: perm_mac,
                current_mac,
            },
            device_type,
            state,
            managed,
            driver,
        });
    }
    Ok(devices)
}

/// Waits for a Wi-Fi device to become ready for operations.
///
/// Uses D-Bus signals to efficiently wait until a Wi-Fi device reaches
/// either Disconnected or Activated state, indicating it's ready for
/// scanning or connection operations. This is useful after enabling Wi-Fi,
/// as the device may take time to initialize.
///
/// Returns `WifiNotReady` if no Wi-Fi device becomes ready within the timeout.
pub(crate) async fn wait_for_wifi_ready(conn: &Connection) -> Result<()> {
    let nm = NMProxy::new(conn).await?;
    let devices = nm.get_devices().await?;

    // Find the Wi-Fi device
    for dev_path in devices {
        let dev = NMDeviceProxy::builder(conn)
            .path(dev_path.clone())?
            .build()
            .await?;

        if dev.device_type().await? != device_type::WIFI {
            continue;
        }

        debug!("Found Wi-Fi device, waiting for it to become ready");

        // Check current state first
        let current_state = dev.state().await?;
        let state = DeviceState::from(current_state);

        if state == DeviceState::Disconnected || state == DeviceState::Activated {
            debug!("Wi-Fi device already ready");
            return Ok(());
        }

        // Wait for device to become ready using signal-based monitoring
        return wait_for_wifi_device_ready(&dev).await;
    }

    Err(ConnectionError::NoWifiDevice)
}

/// Enables or disables Wi-Fi globally.
///
/// This is equivalent to the Wi-Fi toggle in system settings.
/// When disabled, all Wi-Fi connections are terminated and
/// no scanning occurs.
pub(crate) async fn set_wifi_enabled(conn: &Connection, value: bool) -> Result<()> {
    let nm = NMProxy::new(conn).await?;
    Ok(nm.set_wireless_enabled(value).await?)
}

/// Returns whether Wi-Fi is currently enabled.
pub(crate) async fn wifi_enabled(conn: &Connection) -> Result<bool> {
    let nm = NMProxy::new(conn).await?;
    Ok(nm.wireless_enabled().await?)
}
