use std::sync::Arc;

use crate::storage::codec::SolArrayBuilder;
use alloy::{
    dyn_abi::{DynSolEvent, DynSolType, DynSolValue, Specifier},
    hex::ToHexExt,
    json_abi::Event,
    rpc::types::Log,
};
use alloy_primitives::U256;
use anyhow::Result;
use arrow::{
    array::{self, Array, ArrayBuilder, ArrayRef, RecordBatch},
    datatypes::SchemaRef,
};

pub struct BatchEventEncoder {
    dyn_event: DynSolEvent,
    schema: SchemaRef,
    header: Vec<Box<dyn SolArrayBuilder + Send>>, // log_block, log_index, log_address
    indexed: Vec<Box<dyn SolArrayBuilder + Send>>, // topics (indexed parameters)
    data: Vec<Box<dyn SolArrayBuilder + Send>>,   // data (non-indexed parameters)
}

impl BatchEventEncoder {
    pub fn new(event: &Event, schema: SchemaRef) -> Result<Self> {
        let dyn_event = event.resolve().unwrap();

        let header: Vec<Box<dyn SolArrayBuilder + Send>> = vec![
            Box::<SolArrayBuilderUInt64>::default(), // log_block
            Box::<SolArrayBuilderUInt32>::default(), // log_index
            Box::<SolArrayBuilderString>::default(), // log_address
        ];

        let indexed: Vec<Box<dyn SolArrayBuilder + Send>> = event
            .inputs
            .iter()
            .filter(|p| p.indexed)
            .map(|param| dyn_sol_type_to_array_builder(&param.ty.resolve().unwrap()))
            .collect();

        let data: Vec<Box<dyn SolArrayBuilder + Send>> = event
            .inputs
            .iter()
            .filter(|p| !p.indexed)
            .map(|param| dyn_sol_type_to_array_builder(&param.ty.resolve().unwrap()))
            .collect();

        Ok(BatchEventEncoder {
            dyn_event,
            schema,
            header,
            indexed,
            data,
        })
    }

    pub fn append(&mut self, log: &Log) {
        // If the decoded value does not strictly match the event signature, we skip it (indexed params mismatch, etc)
        let decoded = self.dyn_event.decode_log_data(log.data());
        match decoded {
            Ok(event_data) => {
                // TODO: We don't need to convert the log to a DynSolEvent here, we can use the log directly
                self.header[0].append_value(&DynSolValue::Uint(
                    U256::from(log.block_number.unwrap()),
                    64,
                )); // log_block
                self.header[1]
                    .append_value(&DynSolValue::Uint(U256::from(log.log_index.unwrap()), 32)); // log_index
                self.header[2].append_value(&DynSolValue::Address(log.address())); // log_address

                for (val, builder) in event_data.indexed.iter().zip(self.indexed.iter_mut()) {
                    builder.append_value(val);
                }
                for (val, builder) in event_data.body.iter().zip(self.data.iter_mut()) {
                    builder.append_value(val);
                }
            }
            Err(e) => {
                tracing::warn!("Failed to decode log: {:?}", e);
            }
        }
    }

    pub fn finish(&mut self) -> Result<RecordBatch> {
        let columns: Vec<ArrayRef> = self
            .header
            .iter_mut()
            .map(|b| b.finish())
            .chain(self.indexed.iter_mut().map(|b| b.finish()))
            .chain(self.data.iter_mut().map(|b| b.finish()))
            .collect();
        let result = RecordBatch::try_new(self.schema.clone(), columns)?;
        Ok(result)
    }

    pub fn append_iter<'a, I>(&mut self, logs: I) -> Result<()>
    where
        I: IntoIterator<Item = &'a Log>,
    {
        for log in logs {
            self.append(log);
        }
        Ok(())
    }

    pub fn len(&self) -> usize {
        self.header[0].len()
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

fn dyn_sol_type_to_array_builder(ty: &DynSolType) -> Box<dyn SolArrayBuilder + Send> {
    match ty {
        DynSolType::Bool => Box::new(SolArrayBuilderBool::default()),
        DynSolType::Int(n) => {
            if *n > 64 {
                Box::new(SolArrayBuilderString::default())
            } else if *n > 32 {
                Box::new(SolArrayBuilderInt64::default())
            } else if *n > 16 {
                Box::new(SolArrayBuilderInt32::default())
            } else if *n > 8 {
                Box::new(SolArrayBuilderInt16::default())
            } else {
                Box::new(SolArrayBuilderInt8::default())
            }
        }
        DynSolType::Uint(n) => {
            if *n > 64 {
                Box::new(SolArrayBuilderString::default())
            } else if *n > 32 {
                Box::new(SolArrayBuilderUInt64::default())
            } else if *n > 16 {
                Box::new(SolArrayBuilderUInt32::default())
            } else if *n > 8 {
                Box::new(SolArrayBuilderUInt16::default())
            } else {
                Box::new(SolArrayBuilderUInt8::default())
            }
        }
        DynSolType::Address | DynSolType::String => Box::new(SolArrayBuilderString::default()),
        DynSolType::Bytes | DynSolType::FixedBytes(_) => Box::new(SolArrayBuilderBytes::default()),
        _ => unimplemented!(
            "Support for transcoding {ty} solidity type to arrow is not yet implemented",
        ),
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Builders
///////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Default)]
pub struct SolArrayBuilderBool {
    builder: array::BooleanBuilder,
}

impl SolArrayBuilder for SolArrayBuilderBool {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Bool(v) => self.builder.append_value(*v),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Integer Builders
///////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Default)]
struct SolArrayBuilderInt64 {
    builder: array::Int64Builder,
}

impl SolArrayBuilder for SolArrayBuilderInt64 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Int(v, _) => {
                self.builder.append_value(v.low_i64());
            }
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderInt32 {
    builder: array::Int32Builder,
}

impl SolArrayBuilder for SolArrayBuilderInt32 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Int(v, _) => self.builder.append_value(v.low_i32()),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderInt16 {
    builder: array::Int16Builder,
}

impl SolArrayBuilder for SolArrayBuilderInt16 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Int(v, _) => self.builder.append_value(v.low_i16()),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderInt8 {
    builder: array::Int8Builder,
}

impl SolArrayBuilder for SolArrayBuilderInt8 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Int(v, _) => self.builder.append_value(v.low_i8()),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Unsigned Integer Builders
///////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Default)]
struct SolArrayBuilderUInt64 {
    builder: array::UInt64Builder,
}

impl SolArrayBuilder for SolArrayBuilderUInt64 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Uint(v, _) => self.builder.append_value(v.as_limbs()[0]),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderUInt32 {
    builder: array::UInt32Builder,
}

impl SolArrayBuilder for SolArrayBuilderUInt32 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Uint(v, _) => self.builder.append_value(v.as_limbs()[0] as u32),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderUInt16 {
    builder: array::UInt16Builder,
}

impl SolArrayBuilder for SolArrayBuilderUInt16 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Uint(v, _) => self.builder.append_value(v.as_limbs()[0] as u16),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderUInt8 {
    builder: array::UInt8Builder,
}

impl SolArrayBuilder for SolArrayBuilderUInt8 {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Uint(v, _) => self.builder.append_value(v.as_limbs()[0] as u8),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// String and Bytes Builders
///////////////////////////////////////////////////////////////////////////////////////////////////

#[derive(Default)]
struct SolArrayBuilderString {
    builder: array::StringBuilder,
}

impl SolArrayBuilder for SolArrayBuilderString {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Address(v) => self.builder.append_value(v.encode_hex_with_prefix()),
            DynSolValue::String(v) => self.builder.append_value(v),
            DynSolValue::Uint(v, _) => self.builder.append_value(v.to_string()),
            DynSolValue::Int(v, _) => self.builder.append_value(v.to_string()),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[derive(Default)]
struct SolArrayBuilderBytes {
    builder: array::BinaryBuilder,
}

impl SolArrayBuilder for SolArrayBuilderBytes {
    fn append_value(&mut self, value: &DynSolValue) {
        match value {
            DynSolValue::Bytes(v) => self.builder.append_value(v),
            DynSolValue::FixedBytes(v, _) => self.builder.append_value(v.as_slice()),
            _ => panic!("Unexpected value {value:?}"),
        }
    }
    fn len(&self) -> usize {
        self.builder.len()
    }
    fn finish(&mut self) -> Arc<dyn Array> {
        Arc::new(self.builder.finish())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::{Address, B256, I256, U256};
    use arrow::array::{
        BinaryArray, BooleanArray, Int8Array, Int16Array, Int32Array, Int64Array, StringArray,
        UInt8Array, UInt16Array, UInt32Array, UInt64Array,
    };

    ///////////////////////////////////////////////////////////////////////////
    // Bool builder
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_bool_builder_basic() {
        let mut b = SolArrayBuilderBool::default();
        assert!(b.is_empty());
        b.append_value(&DynSolValue::Bool(true));
        b.append_value(&DynSolValue::Bool(false));
        b.append_value(&DynSolValue::Bool(true));
        assert_eq!(b.len(), 3);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<BooleanArray>().unwrap();
        assert!(arr.value(0));
        assert!(!arr.value(1));
        assert!(arr.value(2));
    }

    #[test]
    #[should_panic(expected = "Unexpected value")]
    fn test_bool_builder_wrong_type() {
        let mut b = SolArrayBuilderBool::default();
        b.append_value(&DynSolValue::Uint(U256::from(1), 8));
    }

    ///////////////////////////////////////////////////////////////////////////
    // Signed integer builders
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_int8_builder_basic() {
        let mut b = SolArrayBuilderInt8::default();
        b.append_value(&DynSolValue::Int(I256::try_from(42i8).unwrap(), 8));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 8));
        b.append_value(&DynSolValue::Int(I256::try_from(i8::MAX).unwrap(), 8));
        b.append_value(&DynSolValue::Int(I256::try_from(i8::MIN).unwrap(), 8));
        assert_eq!(b.len(), 4);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int8Array>().unwrap();
        assert_eq!(arr.value(0), 42);
        assert_eq!(arr.value(1), -1);
        assert_eq!(arr.value(2), i8::MAX);
        assert_eq!(arr.value(3), i8::MIN);
    }

    #[test]
    fn test_int8_builder_overflow_truncates() {
        let mut b = SolArrayBuilderInt8::default();
        // Value 256 doesn't fit in i8, should truncate without panic
        b.append_value(&DynSolValue::Int(I256::try_from(256i32).unwrap(), 8));
        assert_eq!(b.len(), 1);
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int8Array>().unwrap();
        assert_eq!(arr.value(0), 0); // 256 truncated to i8 = 0
    }

    #[test]
    fn test_int16_builder_basic() {
        let mut b = SolArrayBuilderInt16::default();
        b.append_value(&DynSolValue::Int(I256::try_from(1000i16).unwrap(), 16));
        b.append_value(&DynSolValue::Int(I256::try_from(-1000i16).unwrap(), 16));
        b.append_value(&DynSolValue::Int(I256::try_from(i16::MAX).unwrap(), 16));
        b.append_value(&DynSolValue::Int(I256::try_from(i16::MIN).unwrap(), 16));
        assert_eq!(b.len(), 4);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int16Array>().unwrap();
        assert_eq!(arr.value(0), 1000);
        assert_eq!(arr.value(1), -1000);
        assert_eq!(arr.value(2), i16::MAX);
        assert_eq!(arr.value(3), i16::MIN);
    }

    #[test]
    fn test_int16_builder_overflow_truncates() {
        let mut b = SolArrayBuilderInt16::default();
        // 0x1_0000 = 65536 doesn't fit in i16, should truncate
        b.append_value(&DynSolValue::Int(I256::try_from(65536i32).unwrap(), 16));
        assert_eq!(b.len(), 1);
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int16Array>().unwrap();
        assert_eq!(arr.value(0), 0); // 65536 truncated to i16 = 0
    }

    #[test]
    fn test_int32_builder_basic() {
        let mut b = SolArrayBuilderInt32::default();
        b.append_value(&DynSolValue::Int(I256::try_from(100_000i32).unwrap(), 24));
        b.append_value(&DynSolValue::Int(I256::try_from(-100_000i32).unwrap(), 24));
        b.append_value(&DynSolValue::Int(I256::try_from(i32::MAX).unwrap(), 32));
        b.append_value(&DynSolValue::Int(I256::try_from(i32::MIN).unwrap(), 32));
        assert_eq!(b.len(), 4);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int32Array>().unwrap();
        assert_eq!(arr.value(0), 100_000);
        assert_eq!(arr.value(1), -100_000);
        assert_eq!(arr.value(2), i32::MAX);
        assert_eq!(arr.value(3), i32::MIN);
    }

    /// Regression: a 160-bit address stuffed into an int24 topic overflowed i32
    #[test]
    fn test_int32_builder_overflow_truncates() {
        let mut b = SolArrayBuilderInt32::default();
        // Address-sized value in a topic that maps to int24 -> Int32 builder
        // 0xa9b1758a3e74e440c774e4cd7d7c00d9c5d27859 is a 160-bit address
        let huge = I256::from_raw(U256::from_be_bytes(
            "0x000000000000000000000000a9b1758a3e74e440c774e4cd7d7c00d9c5d27859"
                .parse::<B256>()
                .unwrap()
                .0,
        ));
        b.append_value(&DynSolValue::Int(huge, 24));
        assert_eq!(b.len(), 1);
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int32Array>().unwrap();
        // Should truncate to low 32 bits without panicking
        assert_eq!(arr.value(0), 0xc5d27859_u32 as i32);
    }

    #[test]
    fn test_int64_builder_basic() {
        let mut b = SolArrayBuilderInt64::default();
        b.append_value(&DynSolValue::Int(I256::try_from(i64::MAX).unwrap(), 64));
        b.append_value(&DynSolValue::Int(I256::try_from(i64::MIN).unwrap(), 64));
        b.append_value(&DynSolValue::Int(I256::ZERO, 64));
        b.append_value(&DynSolValue::Int(I256::MINUS_ONE, 64));
        assert_eq!(b.len(), 4);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int64Array>().unwrap();
        assert_eq!(arr.value(0), i64::MAX);
        assert_eq!(arr.value(1), i64::MIN);
        assert_eq!(arr.value(2), 0);
        assert_eq!(arr.value(3), -1);
    }

    #[test]
    fn test_int64_builder_overflow_truncates() {
        let mut b = SolArrayBuilderInt64::default();
        // Value larger than i64::MAX
        let huge = I256::try_from(u128::from(u64::MAX) + 1).unwrap();
        b.append_value(&DynSolValue::Int(huge, 64));
        assert_eq!(b.len(), 1);
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<Int64Array>().unwrap();
        // 2^64 truncated to i64 = 0
        assert_eq!(arr.value(0), 0);
    }

    #[test]
    #[should_panic(expected = "Unexpected value")]
    fn test_int32_builder_wrong_type() {
        let mut b = SolArrayBuilderInt32::default();
        b.append_value(&DynSolValue::Bool(true));
    }

    ///////////////////////////////////////////////////////////////////////////
    // Unsigned integer builders
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_uint8_builder_basic() {
        let mut b = SolArrayBuilderUInt8::default();
        b.append_value(&DynSolValue::Uint(U256::from(0), 8));
        b.append_value(&DynSolValue::Uint(U256::from(255), 8));
        b.append_value(&DynSolValue::Uint(U256::from(42), 8));
        assert_eq!(b.len(), 3);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt8Array>().unwrap();
        assert_eq!(arr.value(0), 0);
        assert_eq!(arr.value(1), 255);
        assert_eq!(arr.value(2), 42);
    }

    #[test]
    fn test_uint8_builder_overflow_truncates() {
        let mut b = SolArrayBuilderUInt8::default();
        b.append_value(&DynSolValue::Uint(U256::from(256), 8));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt8Array>().unwrap();
        assert_eq!(arr.value(0), 0); // 256 as u8 = 0
    }

    #[test]
    fn test_uint16_builder_basic() {
        let mut b = SolArrayBuilderUInt16::default();
        b.append_value(&DynSolValue::Uint(U256::from(0), 16));
        b.append_value(&DynSolValue::Uint(U256::from(u16::MAX), 16));
        b.append_value(&DynSolValue::Uint(U256::from(1000), 16));
        assert_eq!(b.len(), 3);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt16Array>().unwrap();
        assert_eq!(arr.value(0), 0);
        assert_eq!(arr.value(1), u16::MAX);
        assert_eq!(arr.value(2), 1000);
    }

    #[test]
    fn test_uint16_builder_overflow_truncates() {
        let mut b = SolArrayBuilderUInt16::default();
        b.append_value(&DynSolValue::Uint(U256::from(0x1_0000u32), 16));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt16Array>().unwrap();
        assert_eq!(arr.value(0), 0);
    }

    #[test]
    fn test_uint32_builder_basic() {
        let mut b = SolArrayBuilderUInt32::default();
        b.append_value(&DynSolValue::Uint(U256::from(0), 32));
        b.append_value(&DynSolValue::Uint(U256::from(u32::MAX), 32));
        b.append_value(&DynSolValue::Uint(U256::from(123456), 32));
        assert_eq!(b.len(), 3);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt32Array>().unwrap();
        assert_eq!(arr.value(0), 0);
        assert_eq!(arr.value(1), u32::MAX);
        assert_eq!(arr.value(2), 123456);
    }

    #[test]
    fn test_uint32_builder_overflow_truncates() {
        let mut b = SolArrayBuilderUInt32::default();
        b.append_value(&DynSolValue::Uint(U256::from(0x1_0000_0000u64), 32));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt32Array>().unwrap();
        assert_eq!(arr.value(0), 0);
    }

    #[test]
    fn test_uint64_builder_basic() {
        let mut b = SolArrayBuilderUInt64::default();
        b.append_value(&DynSolValue::Uint(U256::from(0), 64));
        b.append_value(&DynSolValue::Uint(U256::from(u64::MAX), 64));
        b.append_value(&DynSolValue::Uint(U256::from(42), 64));
        assert_eq!(b.len(), 3);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt64Array>().unwrap();
        assert_eq!(arr.value(0), 0);
        assert_eq!(arr.value(1), u64::MAX);
        assert_eq!(arr.value(2), 42);
    }

    #[test]
    fn test_uint64_builder_overflow_truncates() {
        let mut b = SolArrayBuilderUInt64::default();
        // U256 with value in the second limb — low 64 bits are 0
        b.append_value(&DynSolValue::Uint(U256::from(1u128 << 64), 64));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt64Array>().unwrap();
        assert_eq!(arr.value(0), 0);
    }

    #[test]
    #[should_panic(expected = "Unexpected value")]
    fn test_uint32_builder_wrong_type() {
        let mut b = SolArrayBuilderUInt32::default();
        b.append_value(&DynSolValue::Bool(false));
    }

    ///////////////////////////////////////////////////////////////////////////
    // String builder (handles Address, String, Uint>64, Int>64)
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_string_builder_address() {
        let mut b = SolArrayBuilderString::default();
        let addr: Address = "0xdead000000000000000000000000000000000001"
            .parse()
            .unwrap();
        b.append_value(&DynSolValue::Address(addr));
        assert_eq!(b.len(), 1);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<StringArray>().unwrap();
        assert_eq!(arr.value(0), "0xdead000000000000000000000000000000000001");
    }

    #[test]
    fn test_string_builder_string() {
        let mut b = SolArrayBuilderString::default();
        b.append_value(&DynSolValue::String("hello world".to_string()));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<StringArray>().unwrap();
        assert_eq!(arr.value(0), "hello world");
    }

    #[test]
    fn test_string_builder_large_uint() {
        let mut b = SolArrayBuilderString::default();
        let big = U256::from(u128::MAX);
        b.append_value(&DynSolValue::Uint(big, 128));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<StringArray>().unwrap();
        assert_eq!(arr.value(0), u128::MAX.to_string());
    }

    #[test]
    fn test_string_builder_large_int() {
        let mut b = SolArrayBuilderString::default();
        b.append_value(&DynSolValue::Int(I256::MINUS_ONE, 128));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<StringArray>().unwrap();
        assert_eq!(arr.value(0), "-1");
    }

    #[test]
    #[should_panic(expected = "Unexpected value")]
    fn test_string_builder_wrong_type() {
        let mut b = SolArrayBuilderString::default();
        b.append_value(&DynSolValue::Bool(true));
    }

    ///////////////////////////////////////////////////////////////////////////
    // Bytes builder
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_bytes_builder_dynamic() {
        let mut b = SolArrayBuilderBytes::default();
        b.append_value(&DynSolValue::Bytes(vec![0xde, 0xad, 0xbe, 0xef]));
        assert_eq!(b.len(), 1);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<BinaryArray>().unwrap();
        assert_eq!(arr.value(0), &[0xde, 0xad, 0xbe, 0xef]);
    }

    #[test]
    fn test_bytes_builder_fixed() {
        let mut b = SolArrayBuilderBytes::default();
        let mut word = B256::ZERO;
        word.0[..4].copy_from_slice(&[0x01, 0x02, 0x03, 0x04]);
        b.append_value(&DynSolValue::FixedBytes(word, 4));
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<BinaryArray>().unwrap();
        // FixedBytes stores the full 32-byte word via as_slice()
        assert_eq!(&arr.value(0)[..4], &[0x01, 0x02, 0x03, 0x04]);
    }

    #[test]
    #[should_panic(expected = "Unexpected value")]
    fn test_bytes_builder_wrong_type() {
        let mut b = SolArrayBuilderBytes::default();
        b.append_value(&DynSolValue::Uint(U256::from(1), 8));
    }

    ///////////////////////////////////////////////////////////////////////////
    // dyn_sol_type_to_array_builder mapping
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_type_mapping_int_sizes() {
        // int8 -> Int8Builder
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(8));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 8));
        let arr = b.finish();
        assert!(arr.as_any().is::<Int8Array>());

        // int16 -> Int16Builder
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(16));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 16));
        let arr = b.finish();
        assert!(arr.as_any().is::<Int16Array>());

        // int24 -> Int32Builder (24 > 16, 24 <= 32)
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(24));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 24));
        let arr = b.finish();
        assert!(arr.as_any().is::<Int32Array>());

        // int32 -> Int32Builder
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(32));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 32));
        let arr = b.finish();
        assert!(arr.as_any().is::<Int32Array>());

        // int64 -> Int64Builder
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(64));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 64));
        let arr = b.finish();
        assert!(arr.as_any().is::<Int64Array>());

        // int128 -> StringBuilder (> 64)
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(128));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 128));
        let arr = b.finish();
        assert!(arr.as_any().is::<StringArray>());

        // int256 -> StringBuilder
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Int(256));
        b.append_value(&DynSolValue::Int(I256::try_from(-1i8).unwrap(), 256));
        let arr = b.finish();
        assert!(arr.as_any().is::<StringArray>());
    }

    #[test]
    fn test_type_mapping_uint_sizes() {
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Uint(8));
        b.append_value(&DynSolValue::Uint(U256::from(1), 8));
        let arr = b.finish();
        assert!(arr.as_any().is::<UInt8Array>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Uint(16));
        b.append_value(&DynSolValue::Uint(U256::from(1), 16));
        let arr = b.finish();
        assert!(arr.as_any().is::<UInt16Array>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Uint(32));
        b.append_value(&DynSolValue::Uint(U256::from(1), 32));
        let arr = b.finish();
        assert!(arr.as_any().is::<UInt32Array>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Uint(64));
        b.append_value(&DynSolValue::Uint(U256::from(1), 64));
        let arr = b.finish();
        assert!(arr.as_any().is::<UInt64Array>());

        // uint128, uint256 -> String
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Uint(128));
        b.append_value(&DynSolValue::Uint(U256::from(1), 128));
        let arr = b.finish();
        assert!(arr.as_any().is::<StringArray>());
    }

    #[test]
    fn test_type_mapping_non_numeric() {
        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Bool);
        b.append_value(&DynSolValue::Bool(true));
        let arr = b.finish();
        assert!(arr.as_any().is::<BooleanArray>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Address);
        let addr: Address = "0x0000000000000000000000000000000000000001"
            .parse()
            .unwrap();
        b.append_value(&DynSolValue::Address(addr));
        let arr = b.finish();
        assert!(arr.as_any().is::<StringArray>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::Bytes);
        b.append_value(&DynSolValue::Bytes(vec![1]));
        let arr = b.finish();
        assert!(arr.as_any().is::<BinaryArray>());

        let mut b = dyn_sol_type_to_array_builder(&DynSolType::FixedBytes(32));
        b.append_value(&DynSolValue::FixedBytes(B256::ZERO, 32));
        let arr = b.finish();
        assert!(arr.as_any().is::<BinaryArray>());
    }

    ///////////////////////////////////////////////////////////////////////////
    // Builder reset after finish
    ///////////////////////////////////////////////////////////////////////////

    #[test]
    fn test_builder_resets_after_finish() {
        let mut b = SolArrayBuilderUInt32::default();
        b.append_value(&DynSolValue::Uint(U256::from(1), 32));
        b.append_value(&DynSolValue::Uint(U256::from(2), 32));
        assert_eq!(b.len(), 2);

        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt32Array>().unwrap();
        assert_eq!(arr.len(), 2);

        // After finish, builder should be empty and reusable
        assert_eq!(b.len(), 0);
        b.append_value(&DynSolValue::Uint(U256::from(3), 32));
        assert_eq!(b.len(), 1);
        let arr = b.finish();
        let arr = arr.as_any().downcast_ref::<UInt32Array>().unwrap();
        assert_eq!(arr.len(), 1);
        assert_eq!(arr.value(0), 3);
    }
}
