use super::{BidError, BidHandle, BidType};
use crate::box_trait::NyashBox;
use std::collections::HashMap;
use std::sync::Arc;

/// BID-FFI Bridge for Nyash Box types
/// Provides conversion between Nyash runtime values and BID handles
pub trait BidBridge {
    /// Convert a Nyash Box to a BID handle
    fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result<BidHandle, BidError>;

    /// Get the BID type representation
    fn bid_type(&self) -> BidType;
}

/// Registry for managing Box instances and their handles
pub struct BoxRegistry {
    /// Maps handle to Arc<dyn NyashBox>
    handle_to_box: HashMap<BidHandle, Arc<dyn NyashBox>>,

    /// Next instance ID for each type
    next_instance_id: HashMap<u32, u32>,

    /// Reverse lookup: Arc pointer to handle
    box_to_handle: HashMap<usize, BidHandle>,
}

impl BoxRegistry {
    pub fn new() -> Self {
        Self {
            handle_to_box: HashMap::new(),
            next_instance_id: HashMap::new(),
            box_to_handle: HashMap::new(),
        }
    }

    /// Register a Box and get its handle
    pub fn register_box(&mut self, type_id: u32, boxed: Arc<dyn NyashBox>) -> BidHandle {
        // Check if already registered by comparing Arc pointers
        // We use the address of the Arc allocation itself as the key
        let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize;
        if let Some(&handle) = self.box_to_handle.get(&arc_addr) {
            return handle;
        }

        // Generate new instance ID
        let instance_id = self.next_instance_id.entry(type_id).or_insert(1);
        let handle = BidHandle::new(type_id, *instance_id);
        *instance_id += 1;

        // Register bidirectionally
        self.handle_to_box.insert(handle, boxed.clone());
        self.box_to_handle.insert(arc_addr, handle);

        handle
    }

    /// Retrieve a Box by its handle
    pub fn get_box(&self, handle: BidHandle) -> Option<Arc<dyn NyashBox>> {
        self.handle_to_box.get(&handle).cloned()
    }

    /// Remove a Box from the registry
    pub fn unregister(&mut self, handle: BidHandle) -> Option<Arc<dyn NyashBox>> {
        if let Some(boxed) = self.handle_to_box.remove(&handle) {
            let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize;
            self.box_to_handle.remove(&arc_addr);
            Some(boxed)
        } else {
            None
        }
    }
}

/// Convert Nyash Box to BID handle
pub fn box_to_bid_handle(
    arc_box: &Arc<dyn NyashBox>,
    registry: &mut BoxRegistry,
) -> Result<(BidType, BidHandle), BidError> {
    // Downcast to specific box types
    if let Some(_string_box) = arc_box
        .as_any()
        .downcast_ref::<crate::boxes::string_box::StringBox>()
    {
        let handle = registry.register_box(
            crate::bid::types::BoxTypeId::StringBox as u32,
            arc_box.clone(),
        );
        Ok((
            BidType::Handle {
                type_id: 1,
                instance_id: handle.instance_id,
            },
            handle,
        ))
    } else if let Some(_integer_box) = arc_box
        .as_any()
        .downcast_ref::<crate::boxes::integer_box::IntegerBox>()
    {
        let handle = registry.register_box(
            crate::bid::types::BoxTypeId::IntegerBox as u32,
            arc_box.clone(),
        );
        Ok((
            BidType::Handle {
                type_id: 2,
                instance_id: handle.instance_id,
            },
            handle,
        ))
    } else if let Some(_future_box) = arc_box
        .as_any()
        .downcast_ref::<crate::boxes::future::NyashFutureBox>()
    {
        let handle = registry.register_box(
            crate::bid::types::BoxTypeId::FutureBox as u32,
            arc_box.clone(),
        );
        Ok((
            BidType::Handle {
                type_id: 7,
                instance_id: handle.instance_id,
            },
            handle,
        ))
    } else {
        Err(BidError::InvalidType)
    }
}

/// Convert BID handle back to Nyash Box
pub fn bid_handle_to_box(
    handle: BidHandle,
    registry: &BoxRegistry,
) -> Result<Arc<dyn NyashBox>, BidError> {
    registry.get_box(handle).ok_or(BidError::InvalidHandle)
}

/// Extract string value from a Box for TLV encoding
pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidError> {
    if let Some(string_box) = arc_box
        .as_any()
        .downcast_ref::<crate::boxes::string_box::StringBox>()
    {
        Ok(string_box.value.clone())
    } else {
        Err(BidError::InvalidType)
    }
}

/// Extract integer value from a Box for TLV encoding  
pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidError> {
    if let Some(integer_box) = arc_box
        .as_any()
        .downcast_ref::<crate::boxes::integer_box::IntegerBox>()
    {
        Ok(integer_box.value)
    } else {
        Err(BidError::InvalidType)
    }
}

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

    #[test]
    fn test_box_registry() {
        let mut registry = BoxRegistry::new();

        // Create a mock box
        let string_box = crate::boxes::string_box::StringBox::new("Hello");
        let arc_box: Arc<dyn NyashBox> = Arc::new(string_box);

        // Register it
        let handle = registry.register_box(1, arc_box.clone());
        assert_eq!(handle.type_id, 1);
        assert_eq!(handle.instance_id, 1);

        // Retrieve it
        let retrieved = registry.get_box(handle).unwrap();
        assert_eq!(Arc::as_ptr(&retrieved), Arc::as_ptr(&arc_box));

        // Register same box again should return same handle
        let handle2 = registry.register_box(1, arc_box.clone());
        assert_eq!(handle, handle2);
    }

    #[test]
    fn test_string_box_bid_conversion() {
        let mut registry = BoxRegistry::new();

        // Create StringBox
        let string_box = crate::boxes::string_box::StringBox::new("Test String");
        let arc_box: Arc<dyn NyashBox> = Arc::new(string_box);

        // Convert to BID handle
        let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap();
        assert_eq!(handle.type_id, 1); // StringBox type ID
        match bid_type {
            BidType::Handle { type_id, .. } => assert_eq!(type_id, 1),
            _ => panic!("Expected Handle type"),
        }

        // Extract string value
        let value = extract_string_value(&arc_box).unwrap();
        assert_eq!(value, "Test String");

        // Round-trip test
        let retrieved = bid_handle_to_box(handle, &registry).unwrap();
        let retrieved_value = extract_string_value(&retrieved).unwrap();
        assert_eq!(retrieved_value, "Test String");
    }

    #[test]
    fn test_integer_box_bid_conversion() {
        let mut registry = BoxRegistry::new();

        // Create IntegerBox
        let integer_box = crate::boxes::integer_box::IntegerBox::new(42);
        let arc_box: Arc<dyn NyashBox> = Arc::new(integer_box);

        // Convert to BID handle
        let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap();
        assert_eq!(handle.type_id, 2); // IntegerBox type ID
        match bid_type {
            BidType::Handle { type_id, .. } => assert_eq!(type_id, 2),
            _ => panic!("Expected Handle type"),
        }

        // Extract integer value
        let value = extract_integer_value(&arc_box).unwrap();
        assert_eq!(value, 42);

        // Round-trip test
        let retrieved = bid_handle_to_box(handle, &registry).unwrap();
        let retrieved_value = extract_integer_value(&retrieved).unwrap();
        assert_eq!(retrieved_value, 42);
    }

    #[test]
    fn test_future_box_bid_conversion() {
        let mut registry = BoxRegistry::new();

        // Create FutureBox
        let future_box = crate::boxes::future::NyashFutureBox::new();
        let arc_box: Arc<dyn NyashBox> = Arc::new(future_box);

        // Convert to BID handle
        let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap();
        assert_eq!(handle.type_id, 7); // FutureBox type ID
        match bid_type {
            BidType::Handle { type_id, .. } => assert_eq!(type_id, 7),
            _ => panic!("Expected Handle type"),
        }

        // Round-trip test
        let retrieved = bid_handle_to_box(handle, &registry).unwrap();

        // Verify it's still a FutureBox
        assert!(retrieved
            .as_any()
            .downcast_ref::<crate::boxes::future::NyashFutureBox>()
            .is_some());

        // Test with result set
        if let Some(future) = arc_box
            .as_any()
            .downcast_ref::<crate::boxes::future::NyashFutureBox>()
        {
            let string_result = crate::boxes::string_box::StringBox::new("Future Result");
            future.set_result(Box::new(string_result));

            // Verify state
            assert!(future.ready());
            let result = future.get();
            assert_eq!(result.to_string_box().value, "Future Result");
        }
    }
}
