//! SSZ vectors and lists.
//!
//! These wrappers own a `Vec<T>` but enforce SSZ length semantics and provide
//! encode/decode/hash-tree-root implementations for both fixed-size and
//! variable-size element types.
use crate::ssz::hash::{
    BYTES_PER_CHUNK, chunkify_fixed_non_empty, merkleize_with_limit, mix_in_length,
};
use crate::ssz::{HashTreeRoot, SszDecode, SszElement, SszEncode, SszFixedLen};
use crate::types::bytes::Bytes32;
use crate::unsafe_vec::{write_at, write_bytes_at};

/// Fixed-length homogeneous sequence of exactly `LENGTH` elements.
///
/// SSZ encoding: fixed-size elements are concatenated directly; variable-size
/// elements use a 4-byte offset table followed by serialized payloads.
/// `hash_tree_root` merkleizes element roots (or packed bytes for fixed-size
/// primitives) with limit = `LENGTH`. No mix-in-length is applied because the
/// size is static.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SszVector<T, const LENGTH: usize> {
    /// Backing elements in SSZ order.
    data: Vec<T>,
}

/// Variable-length homogeneous sequence bounded by `LIMIT` elements.
///
/// SSZ encoding matches [`SszVector`] but the element count is inferred from
/// the offset table (variable-size) or total byte length (fixed-size).
/// `hash_tree_root` merkleizes element roots with limit = `LIMIT`, then mixes
/// in the actual length as a separate 32-byte little-endian chunk.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SszList<T, const LIMIT: usize> {
    /// Backing elements in SSZ order.
    data: Vec<T>,
}

#[inline]
fn pack_basic_fixed_chunks<T>(items: &[T], elem_len: usize) -> Vec<Bytes32>
where
    T: SszEncode,
{
    let total = items.len() * elem_len;
    if total == 0 {
        return Vec::new();
    }

    if elem_len > BYTES_PER_CHUNK {
        let mut bytes = Vec::with_capacity(total);
        for item in items {
            item.encode_ssz_into(&mut bytes);
        }
        return chunkify_fixed_non_empty(&bytes);
    }

    let chunk_count = total.div_ceil(BYTES_PER_CHUNK);
    let mut chunks = Vec::with_capacity(chunk_count);
    let mut chunk = [0u8; 32];
    let mut filled = 0usize;

    for item in items {
        let mut elem_buf = [0u8; 32];
        unsafe { item.write_fixed_ssz(elem_buf.as_mut_ptr()) };
        let mut src_start = 0usize;

        while src_start < elem_len {
            let space = BYTES_PER_CHUNK - filled;
            let to_copy = (elem_len - src_start).min(space);
            chunk[filled..filled + to_copy]
                .copy_from_slice(&elem_buf[src_start..src_start + to_copy]);
            filled += to_copy;
            src_start += to_copy;

            if filled == BYTES_PER_CHUNK {
                chunks.push(Bytes32::from(chunk));
                chunk = [0u8; 32];
                filled = 0;
            }
        }
    }

    if filled != 0 {
        chunks.push(Bytes32::from(chunk));
    }

    chunks
}

impl<T, const LENGTH: usize> SszVector<T, LENGTH> {
    /// Constructs a vector and enforces the exact SSZ element count.
    pub fn new(data: Vec<T>) -> Result<Self, String> {
        if data.len() != LENGTH {
            return Err(format!(
                "SszVector expects {} elements, got {}",
                LENGTH,
                data.len()
            ));
        }
        Ok(Self { data })
    }

    /// Returns the backing elements as a slice.
    #[inline]
    pub fn as_slice(&self) -> &[T] {
        &self.data
    }

    /// Returns the backing elements as a mutable slice.
    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [T] {
        &mut self.data
    }

    /// Iterates over elements in SSZ order.
    #[inline]
    pub fn iter(&self) -> core::slice::Iter<'_, T> {
        self.data.iter()
    }

    /// Iterates mutably over elements in SSZ order.
    #[inline]
    pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
        self.data.iter_mut()
    }

    /// Returns the exact element count for this vector.
    #[inline]
    pub fn len(&self) -> usize {
        LENGTH
    }

    /// Returns `true` when the vector length is zero.
    #[inline]
    pub fn is_empty(&self) -> bool {
        LENGTH == 0
    }

    /// Consumes the wrapper and returns the backing elements.
    #[inline]
    pub fn into_inner(self) -> Vec<T> {
        self.data
    }

    /// Returns the element at `index`, if present.
    #[inline]
    pub fn get(&self, index: usize) -> Option<&T> {
        self.data.get(index)
    }

    /// Returns the element at `index` mutably, if present.
    #[inline]
    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
        self.data.get_mut(index)
    }

    /// Returns the first element, if present.
    #[inline]
    pub fn first(&self) -> Option<&T> {
        self.data.first()
    }

    /// Returns the first element mutably, if present.
    #[inline]
    pub fn first_mut(&mut self) -> Option<&mut T> {
        self.data.first_mut()
    }

    /// Returns the last element, if present.
    #[inline]
    pub fn last(&self) -> Option<&T> {
        self.data.last()
    }

    /// Returns the last element mutably, if present.
    #[inline]
    pub fn last_mut(&mut self) -> Option<&mut T> {
        self.data.last_mut()
    }

    /// Encodes a fixed-size vector into caller-provided storage without allocation.
    ///
    /// `out` must be exactly `T::fixed_len() * LENGTH` bytes long.
    pub fn encode_ssz_fixed_into(&self, out: &mut [u8])
    where
        T: SszFixedLen + SszEncode + SszElement,
    {
        let elem_len = T::fixed_len();
        let expected = elem_len
            .checked_mul(LENGTH)
            .expect("SszVector fixed length overflows usize");
        debug_assert!(
            out.len() == expected,
            "fixed-size SszVector encode expects {} bytes, got {}",
            expected,
            out.len()
        );
        for (idx, item) in self.data.iter().enumerate() {
            let offset = idx * elem_len;
            unsafe { item.write_fixed_ssz(out.as_mut_ptr().add(offset)) };
        }
    }

    /// Validates vector byte layout before calling raw [`SszDecode`].
    ///
    /// This is mainly useful in tests and harnesses that want checked decoding
    /// without duplicating offset-table validation logic.
    pub fn decode_ssz_checked(bytes: &[u8]) -> Result<Self, String>
    where
        T: SszDecode + SszElement,
    {
        if let Some(elem_len) = T::fixed_len_opt() {
            let expected = elem_len * LENGTH;
            if bytes.len() != expected {
                return Err(format!(
                    "SszVector expects {} bytes, got {}",
                    expected,
                    bytes.len()
                ));
            }
            return Self::decode_ssz(bytes);
        }

        let table_len = 4 * LENGTH;
        if bytes.len() < table_len {
            return Err("SszVector offset table exceeds input length".to_string());
        }

        let off = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
        if off != table_len {
            return Err("SszVector first offset must equal table length".to_string());
        }

        let mut prev = off;
        for i in 1..LENGTH {
            let off_start = i * 4;
            let off_end = off_start + 4;
            let off = u32::from_le_bytes(bytes[off_start..off_end].try_into().unwrap()) as usize;
            if off < prev || off > bytes.len() {
                return Err("SszVector offsets are invalid".to_string());
            }
            prev = off;
        }
        Self::decode_ssz(bytes)
    }
}

impl<T, const LIMIT: usize> Default for SszList<T, LIMIT> {
    fn default() -> Self {
        Self { data: Vec::new() }
    }
}

impl<T, const LIMIT: usize> SszList<T, LIMIT> {
    /// Constructs a list and enforces the SSZ list limit.
    pub fn new(data: Vec<T>) -> Result<Self, String> {
        if data.len() > LIMIT {
            return Err(format!(
                "SszList length {} exceeds limit {}",
                data.len(),
                LIMIT
            ));
        }
        Ok(Self { data })
    }

    /// Returns the backing elements as a slice.
    #[inline]
    pub fn as_slice(&self) -> &[T] {
        &self.data
    }

    /// Returns the backing elements as a mutable slice.
    #[inline]
    pub fn as_mut_slice(&mut self) -> &mut [T] {
        &mut self.data
    }

    /// Iterates over elements in SSZ order.
    #[inline]
    pub fn iter(&self) -> core::slice::Iter<'_, T> {
        self.data.iter()
    }

    /// Iterates mutably over elements in SSZ order.
    #[inline]
    pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, T> {
        self.data.iter_mut()
    }

    /// Returns the current element count.
    #[inline]
    pub fn len(&self) -> usize {
        self.data.len()
    }

    /// Returns `true` when the list has no elements.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    /// Consumes the wrapper and returns the backing elements.
    #[inline]
    pub fn into_inner(self) -> Vec<T> {
        self.data
    }

    /// Returns the element at `index`, if present.
    #[inline]
    pub fn get(&self, index: usize) -> Option<&T> {
        self.data.get(index)
    }

    /// Returns the element at `index` mutably, if present.
    #[inline]
    pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
        self.data.get_mut(index)
    }

    /// Returns the first element, if present.
    #[inline]
    pub fn first(&self) -> Option<&T> {
        self.data.first()
    }

    /// Returns the first element mutably, if present.
    #[inline]
    pub fn first_mut(&mut self) -> Option<&mut T> {
        self.data.first_mut()
    }

    /// Returns the last element, if present.
    #[inline]
    pub fn last(&self) -> Option<&T> {
        self.data.last()
    }

    /// Returns the last element mutably, if present.
    #[inline]
    pub fn last_mut(&mut self) -> Option<&mut T> {
        self.data.last_mut()
    }

    /// Appends an element when doing so stays within `LIMIT`.
    #[inline]
    pub fn push(&mut self, value: T) -> Result<(), String> {
        if self.data.len() == LIMIT {
            return Err(format!("SszList length {} exceeds limit {}", LIMIT + 1, LIMIT));
        }
        self.data.push(value);
        Ok(())
    }

    /// Removes and returns the last element, if present.
    #[inline]
    pub fn pop(&mut self) -> Option<T> {
        self.data.pop()
    }

    /// Truncates the list to `len` elements.
    #[inline]
    pub fn truncate(&mut self, len: usize) {
        self.data.truncate(len);
    }

    /// Removes all elements from the list.
    #[inline]
    pub fn clear(&mut self) {
        self.data.clear();
    }

    /// Validates list byte layout and limit checks before decoding elements.
    ///
    /// This is mainly useful in tests and harnesses that want checked decoding
    /// without duplicating offset-table validation logic.
    pub fn decode_ssz_checked(bytes: &[u8]) -> Result<Self, String>
    where
        T: SszDecode + SszElement,
    {
        if let Some(elem_len) = T::fixed_len_opt() {
            if !bytes.len().is_multiple_of(elem_len) {
                return Err("SszList length is not a multiple of element size".to_string());
            }
            let len = bytes.len() / elem_len;
            if len > LIMIT {
                return Err(format!("SszList length {} exceeds limit {}", len, LIMIT));
            }
            return Self::decode_ssz(bytes);
        }

        if bytes.is_empty() {
            return Ok(Self { data: Vec::new() });
        }
        if bytes.len() < 4 {
            return Err("SszList missing offset table".to_string());
        }

        let first = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
        if !first.is_multiple_of(4) {
            return Err("SszList first offset must be multiple of 4".to_string());
        }
        let len = first / 4;
        if len > LIMIT {
            return Err(format!("SszList length {} exceeds limit {}", len, LIMIT));
        }
        let table_len = 4 * len;
        if first != table_len {
            return Err("SszList first offset must equal table length".to_string());
        }
        if bytes.len() < table_len {
            return Err("SszList offset table exceeds input length".to_string());
        }

        let mut prev = table_len;
        for i in 1..len {
            let off_start = i * 4;
            let off_end = off_start + 4;
            let off = u32::from_le_bytes(bytes[off_start..off_end].try_into().unwrap()) as usize;
            if off < prev || off > bytes.len() {
                return Err("SszList offsets are invalid".to_string());
            }
            prev = off;
        }
        Self::decode_ssz(bytes)
    }
}

impl<T, const LENGTH: usize> SszEncode for SszVector<T, LENGTH>
where
    T: SszEncode + SszElement,
{
    fn encode_ssz(&self) -> Vec<u8> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * LENGTH;
            let mut out: Vec<u8> = Vec::with_capacity(total);
            unsafe { out.set_len(total) };
            for (idx, item) in self.data.iter().enumerate() {
                let offset = idx * elem_len;
                unsafe { item.write_fixed_ssz(out.as_mut_ptr().add(offset)) };
            }
            return out;
        }

        let count = self.data.len();
        let mut offsets = Vec::with_capacity(count);
        let mut elems = Vec::with_capacity(count);
        unsafe {
            offsets.set_len(count);
            elems.set_len(count);
        }
        let mut cursor = 4 * count;
        for (idx, item) in self.data.iter().enumerate() {
            let bytes = item.encode_ssz();
            // SSZ offsets are u32. We keep the direct cast here because the
            // Ethereum-spec objects this crate targets do not approach 4 GiB
            // variable payload sections in practice.
            unsafe { write_at(&mut offsets, idx, cursor as u32) };
            cursor += bytes.len();
            unsafe { write_at(&mut elems, idx, bytes) };
        }
        let mut out = Vec::with_capacity(cursor);
        unsafe { out.set_len(cursor) };
        let mut cursor = 0usize;
        for off in offsets {
            unsafe { write_bytes_at(&mut out, cursor, &off.to_le_bytes()) };
            cursor += 4;
        }
        for bytes in elems {
            unsafe { write_bytes_at(&mut out, cursor, &bytes) };
            cursor += bytes.len();
        }
        out
    }

    fn encode_ssz_checked(&self) -> Result<Vec<u8>, String> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * LENGTH;
            let mut out = Vec::with_capacity(total);
            for item in &self.data {
                let bytes = item.encode_ssz_checked()?;
                if bytes.len() != elem_len {
                    return Err(format!(
                        "fixed-size SszVector element encoded to {} bytes, expected {}",
                        bytes.len(),
                        elem_len
                    ));
                }
                out.extend_from_slice(&bytes);
            }
            return Ok(out);
        }

        let count = self.data.len();
        let mut offsets = Vec::with_capacity(count);
        let mut elems = Vec::with_capacity(count);
        let mut cursor = 4 * count;
        for item in &self.data {
            let bytes = item.encode_ssz_checked()?;
            // SSZ offsets are u32. We keep the direct cast here because the
            // Ethereum-spec objects this crate targets do not approach 4 GiB
            // variable payload sections in practice.
            offsets.push(cursor as u32);
            cursor += bytes.len();
            elems.push(bytes);
        }
        let mut out = Vec::with_capacity(cursor);
        unsafe { out.set_len(cursor) };
        let mut cursor = 0usize;
        for off in offsets {
            unsafe { write_bytes_at(&mut out, cursor, &off.to_le_bytes()) };
            cursor += 4;
        }
        for bytes in elems {
            unsafe { write_bytes_at(&mut out, cursor, &bytes) };
            cursor += bytes.len();
        }
        Ok(out)
    }

    fn encode_ssz_into(&self, out: &mut Vec<u8>) {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * LENGTH;
            let start = out.len();
            out.reserve(total);
            unsafe { out.set_len(start + total) };
            for (idx, item) in self.data.iter().enumerate() {
                let offset = start + idx * elem_len;
                unsafe { item.write_fixed_ssz(out.as_mut_ptr().add(offset)) };
            }
            return;
        }

        let count = self.data.len();
        let table_start = out.len();
        let table_len = 4 * count;
        out.reserve(table_len);
        unsafe { out.set_len(table_start + table_len) };
        for (idx, item) in self.data.iter().enumerate() {
            let offset = (out.len() - table_start) as u32;
            unsafe { write_bytes_at(out, table_start + idx * 4, &offset.to_le_bytes()) };
            item.encode_ssz_into(out);
        }
    }

    unsafe fn write_fixed_ssz(&self, dst: *mut u8) {
        let Some(elem_len) = T::fixed_len_opt() else {
            panic!("variable-size SszVector cannot be written via write_fixed_ssz");
        };
        for (idx, item) in self.data.iter().enumerate() {
            let offset = idx * elem_len;
            unsafe { item.write_fixed_ssz(dst.add(offset)) };
        }
    }
}

impl<T, const LIMIT: usize> SszEncode for SszList<T, LIMIT>
where
    T: SszEncode + SszElement,
{
    fn encode_ssz(&self) -> Vec<u8> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * self.data.len();
            let mut out: Vec<u8> = Vec::with_capacity(total);
            unsafe { out.set_len(total) };
            for (idx, item) in self.data.iter().enumerate() {
                let offset = idx * elem_len;
                unsafe { item.write_fixed_ssz(out.as_mut_ptr().add(offset)) };
            }
            return out;
        }

        let count = self.data.len();
        let mut offsets = Vec::with_capacity(count);
        let mut elems = Vec::with_capacity(count);
        unsafe {
            offsets.set_len(count);
            elems.set_len(count);
        }
        let mut cursor = 4 * count;
        for (idx, item) in self.data.iter().enumerate() {
            let bytes = item.encode_ssz();
            unsafe { write_at(&mut offsets, idx, cursor as u32) };
            cursor += bytes.len();
            unsafe { write_at(&mut elems, idx, bytes) };
        }
        let mut out = Vec::with_capacity(cursor);
        let table_len = 4 * count;
        unsafe { out.set_len(cursor) };
        for (idx, off) in offsets.iter().enumerate() {
            unsafe { write_bytes_at(&mut out, idx * 4, &off.to_le_bytes()) };
        }
        let mut payload_cursor = table_len;
        for bytes in elems {
            unsafe { write_bytes_at(&mut out, payload_cursor, &bytes) };
            payload_cursor += bytes.len();
        }
        out
    }

    fn encode_ssz_checked(&self) -> Result<Vec<u8>, String> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * self.data.len();
            let mut out = Vec::with_capacity(total);
            for item in &self.data {
                let bytes = item.encode_ssz_checked()?;
                if bytes.len() != elem_len {
                    return Err(format!(
                        "fixed-size SszList element encoded to {} bytes, expected {}",
                        bytes.len(),
                        elem_len
                    ));
                }
                out.extend_from_slice(&bytes);
            }
            return Ok(out);
        }

        let count = self.data.len();
        let mut offsets = Vec::with_capacity(count);
        let mut elems = Vec::with_capacity(count);
        let mut cursor = 4 * count;
        for item in &self.data {
            let bytes = item.encode_ssz_checked()?;
            // SSZ offsets are u32. We keep the direct cast here because the
            // Ethereum-spec objects this crate targets do not approach 4 GiB
            // variable payload sections in practice.
            offsets.push(cursor as u32);
            cursor += bytes.len();
            elems.push(bytes);
        }
        let mut out = Vec::with_capacity(cursor);
        let table_len = 4 * count;
        unsafe { out.set_len(cursor) };
        for (idx, off) in offsets.iter().enumerate() {
            unsafe { write_bytes_at(&mut out, idx * 4, &off.to_le_bytes()) };
        }
        let mut payload_cursor = table_len;
        for bytes in elems {
            unsafe { write_bytes_at(&mut out, payload_cursor, &bytes) };
            payload_cursor += bytes.len();
        }
        Ok(out)
    }

    fn encode_ssz_into(&self, out: &mut Vec<u8>) {
        if let Some(elem_len) = T::fixed_len_opt() {
            let total = elem_len * self.data.len();
            let start = out.len();
            out.reserve(total);
            unsafe { out.set_len(start + total) };
            for (idx, item) in self.data.iter().enumerate() {
                let offset = start + idx * elem_len;
                unsafe { item.write_fixed_ssz(out.as_mut_ptr().add(offset)) };
            }
            return;
        }

        let count = self.data.len();
        let table_start = out.len();
        let table_len = 4 * count;
        out.reserve(table_len);
        unsafe { out.set_len(table_start + table_len) };
        for (idx, item) in self.data.iter().enumerate() {
            let offset = (out.len() - table_start) as u32;
            unsafe { write_bytes_at(out, table_start + idx * 4, &offset.to_le_bytes()) };
            item.encode_ssz_into(out);
        }
    }

    unsafe fn write_fixed_ssz(&self, _dst: *mut u8) {
        panic!("SszList is variable-size and cannot be written via write_fixed_ssz");
    }
}

impl<T, const LENGTH: usize> SszDecode for SszVector<T, LENGTH>
where
    T: SszDecode + SszElement,
{
    fn decode_ssz(bytes: &[u8]) -> Result<Self, String> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let mut data = Vec::with_capacity(LENGTH);
            unsafe { data.set_len(LENGTH) };
            for i in 0..LENGTH {
                let start = i * elem_len;
                let end = start + elem_len;
                let x = match T::decode_ssz(&bytes[start..end]) {
                    Ok(val) => val,
                    Err(err) => {
                        unsafe {
                            let ptr: *mut T = data.as_mut_ptr();
                            for j in 0..i {
                                core::ptr::drop_in_place(ptr.add(j));
                            }
                            data.set_len(0);
                        }
                        return Err(err);
                    }
                };
                unsafe { write_at(&mut data, i, x) };
            }
            return Ok(Self { data });
        }

        let mut data = Vec::with_capacity(LENGTH);
        unsafe { data.set_len(LENGTH) };
        for i in 0..LENGTH {
            let off_start = i * 4;
            let off_end = off_start + 4;
            let start = u32::from_le_bytes(bytes[off_start..off_end].try_into().unwrap()) as usize;
            let end = if i + 1 < LENGTH {
                let next_start = (i + 1) * 4;
                let next_end = next_start + 4;
                u32::from_le_bytes(bytes[next_start..next_end].try_into().unwrap()) as usize
            } else {
                bytes.len()
            };
            let x = match T::decode_ssz(&bytes[start..end]) {
                Ok(val) => val,
                Err(err) => {
                    unsafe {
                        let ptr: *mut T = data.as_mut_ptr();
                        for j in 0..i {
                            core::ptr::drop_in_place(ptr.add(j));
                        }
                        data.set_len(0);
                    }
                    return Err(err);
                }
            };
            unsafe { write_at(&mut data, i, x) };
        }
        Ok(Self { data })
    }
}

impl<T, const LIMIT: usize> SszDecode for SszList<T, LIMIT>
where
    T: SszDecode + SszElement,
{
    fn decode_ssz(bytes: &[u8]) -> Result<Self, String> {
        if let Some(elem_len) = T::fixed_len_opt() {
            let len = bytes.len() / elem_len;
            let mut data = Vec::with_capacity(len);
            unsafe { data.set_len(len) };
            for i in 0..len {
                let start = i * elem_len;
                let end = start + elem_len;
                let x = match T::decode_ssz(&bytes[start..end]) {
                    Ok(val) => val,
                    Err(err) => {
                        unsafe {
                            let ptr: *mut T = data.as_mut_ptr();
                            for j in 0..i {
                                core::ptr::drop_in_place(ptr.add(j));
                            }
                            data.set_len(0);
                        }
                        return Err(err);
                    }
                };
                unsafe { write_at(&mut data, i, x) };
            }
            return Ok(Self { data });
        }

        if bytes.is_empty() {
            return Ok(Self { data: Vec::new() });
        }

        let first = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) as usize;
        let len = first / 4;
        let mut data = Vec::with_capacity(len);
        unsafe { data.set_len(len) };
        for i in 0..len {
            let off_start = i * 4;
            let off_end = off_start + 4;
            let start = u32::from_le_bytes(bytes[off_start..off_end].try_into().unwrap()) as usize;
            let end = if i + 1 < len {
                let next_start = (i + 1) * 4;
                let next_end = next_start + 4;
                u32::from_le_bytes(bytes[next_start..next_end].try_into().unwrap()) as usize
            } else {
                bytes.len()
            };
            let x = match T::decode_ssz(&bytes[start..end]) {
                Ok(val) => val,
                Err(err) => {
                    unsafe {
                        let ptr: *mut T = data.as_mut_ptr();
                        for j in 0..i {
                            core::ptr::drop_in_place(ptr.add(j));
                        }
                        data.set_len(0);
                    }
                    return Err(err);
                }
            };
            unsafe { write_at(&mut data, i, x) };
        }
        Ok(Self { data })
    }
}

impl<T, const LENGTH: usize> HashTreeRoot for SszVector<T, LENGTH>
where
    T: SszEncode + SszElement + HashTreeRoot,
{
    fn hash_tree_root(&self) -> [u8; 32] {
        if let Some(elem_len) = T::fixed_len_opt().filter(|_| T::tree_pack_basic()) {
            let chunks = pack_basic_fixed_chunks(&self.data, elem_len);
            let limit_chunks = (LENGTH * elem_len).div_ceil(BYTES_PER_CHUNK);
            let root =
                merkleize_with_limit(&chunks, limit_chunks).unwrap_or_else(|_| Bytes32::zero());
            return *root.as_ref();
        }

        let count = self.data.len();
        let mut chunks = Vec::with_capacity(count);
        unsafe { chunks.set_len(count) };
        for (i, item) in self.data.iter().enumerate() {
            let root = Bytes32::from(item.hash_tree_root());
            unsafe { write_at(&mut chunks, i, root) };
        }
        let root = merkleize_with_limit(&chunks, LENGTH).unwrap_or_else(|_| Bytes32::zero());
        *root.as_ref()
    }
}

impl<T, const LIMIT: usize> HashTreeRoot for SszList<T, LIMIT>
where
    T: SszEncode + SszElement + HashTreeRoot,
{
    fn hash_tree_root(&self) -> [u8; 32] {
        if let Some(elem_len) = T::fixed_len_opt().filter(|_| T::tree_pack_basic()) {
            let chunks = pack_basic_fixed_chunks(&self.data, elem_len);
            let limit_chunks = (LIMIT * elem_len).div_ceil(BYTES_PER_CHUNK);
            let root =
                merkleize_with_limit(&chunks, limit_chunks).unwrap_or_else(|_| Bytes32::zero());
            let mixed = mix_in_length(&root, self.data.len());
            return *mixed.as_ref();
        }

        let count = self.data.len();
        let mut chunks = Vec::with_capacity(count);
        unsafe { chunks.set_len(count) };
        for (i, item) in self.data.iter().enumerate() {
            let root = Bytes32::from(item.hash_tree_root());
            unsafe { write_at(&mut chunks, i, root) };
        }
        let root = merkleize_with_limit(&chunks, LIMIT).unwrap_or_else(|_| Bytes32::zero());
        let mixed = mix_in_length(&root, count);
        *mixed.as_ref()
    }
}

impl<T, const LENGTH: usize> SszElement for SszVector<T, LENGTH>
where
    T: SszElement,
{
    fn fixed_len_opt() -> Option<usize> {
        if LENGTH == 0 {
            // Zero-length vectors encode to empty bytes, but treating them as
            // fixed-size list elements would make `SszList<SszVector<_, 0>, _>`
            // ambiguous to decode because every list length would serialize to
            // the same empty payload. Keep them on the offset-table path.
            return None;
        }
        T::fixed_len_opt().and_then(|elem_len| elem_len.checked_mul(LENGTH))
    }
}

impl<T, const LIMIT: usize> SszElement for SszList<T, LIMIT> {}

#[cfg(test)]
mod tests {
    use super::{SszList, SszVector};
    use crate::ssz::SszEncode;
    use crate::types::bitlist::BitVector;

    #[test]
    fn list_encode_into_matches_encode_ssz_for_nested_lists() {
        let inner = SszList::<u64, 8>::new(vec![1, 2, 3, 4]).unwrap();
        let outer = SszList::<SszList<u64, 8>, 4>::new(vec![inner.clone(), inner]).unwrap();

        let mut out = Vec::new();
        outer.encode_ssz_into(&mut out);

        assert_eq!(out, outer.encode_ssz());
    }

    #[test]
    fn vector_encode_into_matches_encode_ssz_for_nested_lists() {
        let inner = SszList::<u64, 8>::new(vec![1, 2, 3, 4]).unwrap();
        let vector = SszVector::<SszList<u64, 8>, 2>::new(vec![inner.clone(), inner]).unwrap();

        let mut out = Vec::new();
        vector.encode_ssz_into(&mut out);

        assert_eq!(out, vector.encode_ssz());
    }

    #[test]
    fn list_of_fixed_vectors_encodes_without_offsets() {
        let first = SszVector::<u64, 2>::new(vec![1, 2]).unwrap();
        let second = SszVector::<u64, 2>::new(vec![3, 4]).unwrap();
        let outer = SszList::<SszVector<u64, 2>, 4>::new(vec![first, second]).unwrap();

        let mut expected = Vec::new();
        for value in [1u64, 2, 3, 4] {
            expected.extend_from_slice(&value.to_le_bytes());
        }

        assert_eq!(outer.encode_ssz(), expected);
    }

    #[test]
    fn list_of_zero_length_vectors_uses_offsets_and_round_trips() {
        let first = SszVector::<u64, 0>::new(vec![]).unwrap();
        let second = SszVector::<u64, 0>::new(vec![]).unwrap();
        let outer = SszList::<SszVector<u64, 0>, 4>::new(vec![first, second]).unwrap();

        assert_eq!(outer.encode_ssz(), vec![8, 0, 0, 0, 8, 0, 0, 0]);
        let decoded = SszList::<SszVector<u64, 0>, 4>::decode_ssz_checked(&outer.encode_ssz())
            .expect("zero-length vector list should decode");
        assert_eq!(decoded, outer);
    }

    #[test]
    fn list_encode_ssz_checked_validates_elements() {
        let invalid = BitVector::<3> { data: vec![0xff] };
        let list = SszList::<BitVector<3>, 4>::new(vec![invalid]).unwrap();

        assert_eq!(
            list.encode_ssz_checked().unwrap_err(),
            "BitVector has non-zero unused bits"
        );
    }

    #[test]
    fn vector_encode_ssz_checked_validates_elements() {
        let invalid = BitVector::<3> { data: vec![0xff] };
        let vector = SszVector::<BitVector<3>, 1>::new(vec![invalid]).unwrap();

        assert_eq!(
            vector.encode_ssz_checked().unwrap_err(),
            "BitVector has non-zero unused bits"
        );
    }

    #[test]
    fn fixed_vector_fixed_encode_matches_vec_encode() {
        let vector = SszVector::<u64, 2>::new(vec![1, 2]).unwrap();

        let mut out = [0u8; 16];
        vector.encode_ssz_fixed_into(&mut out);

        assert_eq!(out.as_slice(), vector.encode_ssz().as_slice());
    }

    #[test]
    fn vector_mut_access_preserves_length_invariant() {
        let mut vector = SszVector::<u64, 2>::new(vec![1, 2]).unwrap();
        vector.as_mut_slice()[0] = 9;
        *vector.get_mut(1).unwrap() = 7;

        assert_eq!(vector.as_slice(), &[9, 7]);
        assert_eq!(vector.len(), 2);
    }

    #[test]
    fn list_push_respects_limit() {
        let mut list = SszList::<u64, 2>::new(vec![1]).unwrap();
        list.push(2).unwrap();

        assert_eq!(list.as_slice(), &[1, 2]);
        assert_eq!(
            list.push(3).unwrap_err(),
            "SszList length 3 exceeds limit 2"
        );
    }

    #[test]
    fn list_mutators_update_contents_without_exposing_backing_vec() {
        let mut list = SszList::<u64, 4>::new(vec![1, 2, 3]).unwrap();
        *list.first_mut().unwrap() = 10;
        *list.last_mut().unwrap() = 30;
        list.truncate(2);
        list.push(40).unwrap();
        assert_eq!(list.pop(), Some(40));
        list.clear();

        assert!(list.is_empty());
    }
}
