//! Device registry and management.
//!
//! Supports all device types:
//! - Data center servers
//! - Desktop workstations
//! - Laptops
//! - Mobile devices (iOS, Android)
//! - Browsers (WebGPU, WASM)
//! - IoT devices

use crate::error::ComputeError;
use crate::processor::{GenericProcessor, Processor, ProcessorId, ProcessorType};
use crate::{NodeId, ProcessorInfo};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;

/// Unique device identifier.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct DeviceId(pub [u8; 32]);

impl DeviceId {
    /// Creates a new random device ID.
    pub fn new() -> Self {
        use rand::Rng;
        let mut bytes = [0u8; 32];
        rand::thread_rng().fill(&mut bytes);
        DeviceId(bytes)
    }

    /// Creates from bytes.
    pub fn from_bytes(bytes: [u8; 32]) -> Self {
        DeviceId(bytes)
    }
}

impl Default for DeviceId {
    fn default() -> Self {
        Self::new()
    }
}

impl std::fmt::Display for DeviceId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "dev_{}", hex::encode(&self.0[..8]))
    }
}

/// Device type classification.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DeviceType {
    /// Data center server.
    DataCenter,
    /// Desktop workstation.
    Desktop,
    /// Laptop.
    Laptop,
    /// Mobile phone.
    Mobile,
    /// Tablet.
    Tablet,
    /// IoT device.
    IoT,
    /// Browser (WebGPU/WASM).
    Browser,
    /// Edge server.
    Edge,
}

impl DeviceType {
    /// Returns typical reliability score (0-100).
    pub fn reliability(&self) -> u32 {
        match self {
            DeviceType::DataCenter => 99,
            DeviceType::Edge => 95,
            DeviceType::Desktop => 80,
            DeviceType::Laptop => 60,
            DeviceType::Mobile => 40,
            DeviceType::Tablet => 50,
            DeviceType::IoT => 70,
            DeviceType::Browser => 30,
        }
    }

    /// Returns typical availability hours per day.
    pub fn availability_hours(&self) -> f32 {
        match self {
            DeviceType::DataCenter => 24.0,
            DeviceType::Edge => 24.0,
            DeviceType::Desktop => 8.0,
            DeviceType::Laptop => 6.0,
            DeviceType::Mobile => 4.0,
            DeviceType::Tablet => 4.0,
            DeviceType::IoT => 24.0,
            DeviceType::Browser => 2.0,
        }
    }
}

/// Device capabilities.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DeviceCapabilities {
    /// Device type.
    pub device_type: DeviceType,
    /// Available processors.
    pub processors: Vec<ProcessorType>,
    /// Total memory (GB).
    pub memory_gb: f32,
    /// Network bandwidth (Mbps).
    pub bandwidth_mbps: f32,
    /// Storage available (GB).
    pub storage_gb: f32,
    /// Battery powered.
    pub battery_powered: bool,
    /// Supports background execution.
    pub background_execution: bool,
}

/// Device information.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DeviceInfo {
    /// Device ID.
    pub id: DeviceId,
    /// Device type.
    pub device_type: DeviceType,
    /// Owner address.
    pub owner: [u8; 32],
    /// Capabilities.
    pub capabilities: DeviceCapabilities,
    /// Current status.
    pub status: DeviceStatus,
    /// Reputation score (0-100).
    pub reputation: u32,
    /// Total earnings (atomic SYNOR).
    pub earnings: u64,
    /// Geographic region.
    pub region: String,
}

/// Device status.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DeviceStatus {
    /// Online and available.
    Online,
    /// Online but busy.
    Busy,
    /// Idle but available.
    Idle,
    /// On battery (reduced capacity).
    OnBattery,
    /// Offline.
    Offline,
    /// Maintenance.
    Maintenance,
}

/// Device registry managing all devices and processors.
pub struct DeviceRegistry {
    /// Registered devices.
    devices: RwLock<HashMap<DeviceId, DeviceInfo>>,
    /// Node to device mapping.
    node_devices: RwLock<HashMap<NodeId, Vec<DeviceId>>>,
    /// All processors (across all nodes).
    processors: RwLock<HashMap<ProcessorId, Arc<dyn Processor>>>,
    /// Processor to node mapping.
    processor_nodes: RwLock<HashMap<ProcessorId, NodeId>>,
    /// Next processor ID.
    next_processor_id: std::sync::atomic::AtomicU64,
}

impl DeviceRegistry {
    /// Creates a new device registry.
    pub fn new() -> Self {
        Self {
            devices: RwLock::new(HashMap::new()),
            node_devices: RwLock::new(HashMap::new()),
            processors: RwLock::new(HashMap::new()),
            processor_nodes: RwLock::new(HashMap::new()),
            next_processor_id: std::sync::atomic::AtomicU64::new(0),
        }
    }

    /// Registers a device.
    pub fn register_device(&self, device: DeviceInfo) -> Result<DeviceId, ComputeError> {
        let id = device.id;
        self.devices.write().insert(id, device);
        Ok(id)
    }

    /// Unregisters a device.
    pub fn unregister_device(&self, device_id: DeviceId) -> Result<(), ComputeError> {
        self.devices.write().remove(&device_id);
        Ok(())
    }

    /// Gets a device by ID.
    pub fn get_device(&self, device_id: DeviceId) -> Option<DeviceInfo> {
        self.devices.read().get(&device_id).cloned()
    }

    /// Registers a processor for a node.
    pub fn register_processor(
        &self,
        node_id: NodeId,
        info: ProcessorInfo,
    ) -> Result<(), ComputeError> {
        let processor_id = info.id;

        // Create a generic processor from the info
        let processor: Arc<dyn Processor> = Arc::new(GenericProcessor::new(
            processor_id,
            info.processor_type,
            info.capabilities,
        ));

        self.processors.write().insert(processor_id, processor);
        self.processor_nodes.write().insert(processor_id, node_id);

        Ok(())
    }

    /// Unregisters all processors for a node.
    pub fn unregister_node(&self, node_id: NodeId) -> Result<(), ComputeError> {
        let mut processors = self.processors.write();
        let mut processor_nodes = self.processor_nodes.write();

        // Find and remove all processors for this node
        let to_remove: Vec<_> = processor_nodes
            .iter()
            .filter(|(_, n)| **n == node_id)
            .map(|(p, _)| *p)
            .collect();

        for proc_id in to_remove {
            processors.remove(&proc_id);
            processor_nodes.remove(&proc_id);
        }

        Ok(())
    }

    /// Gets a processor by ID.
    pub fn get_processor(&self, processor_id: ProcessorId) -> Result<Arc<dyn Processor>, ComputeError> {
        self.processors
            .read()
            .get(&processor_id)
            .cloned()
            .ok_or(ComputeError::ProcessorNotFound(processor_id))
    }

    /// Gets all processors.
    pub fn all_processors(&self) -> Vec<Arc<dyn Processor>> {
        self.processors.read().values().cloned().collect()
    }

    /// Gets processors of a specific type.
    pub fn processors_by_type(&self, proc_type: ProcessorType) -> Vec<Arc<dyn Processor>> {
        self.processors
            .read()
            .values()
            .filter(|p| p.processor_type() == proc_type)
            .cloned()
            .collect()
    }

    /// Gets the next processor ID.
    pub fn next_processor_id(&self) -> ProcessorId {
        ProcessorId(self.next_processor_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
    }

    /// Gets total number of devices.
    pub fn device_count(&self) -> usize {
        self.devices.read().len()
    }

    /// Gets total number of processors.
    pub fn processor_count(&self) -> usize {
        self.processors.read().len()
    }

    /// Gets devices by type.
    pub fn devices_by_type(&self, device_type: DeviceType) -> Vec<DeviceInfo> {
        self.devices
            .read()
            .values()
            .filter(|d| d.device_type == device_type)
            .cloned()
            .collect()
    }

    /// Gets online devices.
    pub fn online_devices(&self) -> Vec<DeviceInfo> {
        self.devices
            .read()
            .values()
            .filter(|d| d.status == DeviceStatus::Online || d.status == DeviceStatus::Idle)
            .cloned()
            .collect()
    }

    /// Updates device status.
    pub fn update_device_status(
        &self,
        device_id: DeviceId,
        status: DeviceStatus,
    ) -> Result<(), ComputeError> {
        if let Some(device) = self.devices.write().get_mut(&device_id) {
            device.status = status;
            Ok(())
        } else {
            Err(ComputeError::Internal(format!("Device not found: {}", device_id)))
        }
    }
}

impl Default for DeviceRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::processor::{CpuVariant, AvxSupport};

    #[test]
    fn test_device_id() {
        let id1 = DeviceId::new();
        let id2 = DeviceId::new();
        assert_ne!(id1.0, id2.0);
    }

    #[test]
    fn test_device_registry() {
        let registry = DeviceRegistry::new();

        let device = DeviceInfo {
            id: DeviceId::new(),
            device_type: DeviceType::Desktop,
            owner: [1u8; 32],
            capabilities: DeviceCapabilities {
                device_type: DeviceType::Desktop,
                processors: vec![ProcessorType::Cpu(CpuVariant::X86_64 {
                    avx: AvxSupport::Avx512,
                })],
                memory_gb: 64.0,
                bandwidth_mbps: 1000.0,
                storage_gb: 1000.0,
                battery_powered: false,
                background_execution: true,
            },
            status: DeviceStatus::Online,
            reputation: 100,
            earnings: 0,
            region: "us-east".to_string(),
        };

        let device_id = device.id;
        registry.register_device(device).unwrap();

        assert_eq!(registry.device_count(), 1);
        assert!(registry.get_device(device_id).is_some());

        registry.unregister_device(device_id).unwrap();
        assert_eq!(registry.device_count(), 0);
    }

    #[test]
    fn test_device_type_properties() {
        assert_eq!(DeviceType::DataCenter.reliability(), 99);
        assert_eq!(DeviceType::Mobile.reliability(), 40);
        assert_eq!(DeviceType::DataCenter.availability_hours(), 24.0);
        assert_eq!(DeviceType::Browser.availability_hours(), 2.0);
    }
}
