use super::{
    StorageClearable, StorageMapper, StorageMapperFromAddress,
    source::{CurrentStorage, StorageAddress},
};
use crate::{
    abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName},
    api::{ErrorApiImpl, StorageMapperApi},
    codec::{
        EncodeErrorHandler, TopDecode, TopEncode, TopEncodeMulti, TopEncodeMultiOutput,
        multi_encode_iter_or_handle_err,
    },
    storage::{StorageKey, storage_clear, storage_set},
    types::{ManagedAddress, ManagedType, MultiValueEncoded},
};
use core::marker::PhantomData;

const LEN_SUFFIX: &str = ".len";
const ITEM_SUFFIX: &str = ".item";

const INDEX_OUT_OF_RANGE_ERR_MSG: &str = "index out of range";

/// A storage mapper for managing an ordered, indexable list of items with automatic length tracking.
///
/// # Storage Layout
///
/// The `VecMapper` stores data in storage using the following pattern:
///
/// - `base_key + ".len"` → number of items in the list
/// - `base_key + ".item" + index` → value at index (1-based indexing)
///
/// **Important**: Indexes start from 1, not 0. This avoids confusion between uninitialized variables
/// and the zero value. Valid indexes are always in the range `1..=len()`.
///
/// # Main Operations
///
/// - **Append**: `push(item)` - Adds an item to the end. O(1) with 2 storage writes (item + length).
/// - **Random Access**: `get(index)` - Retrieves value at index (1-based). O(1) with one storage read.
/// - **Update**: `set(index, item)` - Replaces value at index. O(1) with one storage write.
/// - **Remove**: `swap_remove(index)` - Removes by swapping with last element. O(1) with storage writes.
/// - **Iteration**: `iter()` - Iterates over all values in order from index 1 to length.
/// - **Batch Operations**: `extend_from_slice(items)` - Adds multiple items efficiently (single length update).
///
/// # Trade-offs
///
/// - **Pros**: Maintains insertion order; efficient random access; automatic length tracking.
/// - **Cons**: Uses 1-based indexing (not 0-based); removal (except last item) changes element positions;
///   clearing large lists can consume significant gas.
///
/// # Example
///
/// ```rust
/// # use multiversx_sc::storage::mappers::{StorageMapper, VecMapper};
/// # fn example<SA: multiversx_sc::api::StorageMapperApi>() {
/// # let mut mapper = VecMapper::<SA, u64>::new(
/// #     multiversx_sc::storage::StorageKey::new(&b"my_vec"[..])
/// # );
/// mapper.push(&100);
/// mapper.push(&200);
/// mapper.push(&300);
///
/// assert_eq!(mapper.len(), 3);
/// assert_eq!(mapper.get(1), 100);  // Note: 1-based indexing
/// assert_eq!(mapper.get(2), 200);
///
/// mapper.set(2, &250);
/// assert_eq!(mapper.get(2), 250);
///
/// mapper.swap_remove(1);  // Removes 100, moves 300 to index 1
/// assert_eq!(mapper.len(), 2);
/// assert_eq!(mapper.get(1), 300);
/// # }
/// ```
pub struct VecMapper<SA, T, A = CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode + 'static,
{
    _phantom_api: PhantomData<SA>,
    address: A,
    base_key: StorageKey<SA>,
    len_key: StorageKey<SA>,
    _phantom_item: PhantomData<T>,
}

impl<SA, T> StorageMapper<SA> for VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
    fn new(base_key: StorageKey<SA>) -> Self {
        let mut len_key = base_key.clone();
        len_key.append_bytes(LEN_SUFFIX.as_bytes());

        VecMapper {
            _phantom_api: PhantomData,
            address: CurrentStorage,
            base_key,
            len_key,
            _phantom_item: PhantomData,
        }
    }
}

impl<SA, T> StorageMapperFromAddress<SA> for VecMapper<SA, T, ManagedAddress<SA>>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
        let mut len_key = base_key.clone();
        len_key.append_bytes(LEN_SUFFIX.as_bytes());

        VecMapper {
            _phantom_api: PhantomData,
            address,
            base_key,
            len_key,
            _phantom_item: PhantomData,
        }
    }
}

impl<SA, T> StorageClearable for VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
    fn clear(&mut self) {
        self.clear();
    }
}

impl<SA, T, A> VecMapper<SA, T, A>
where
    SA: StorageMapperApi,
    A: StorageAddress<SA>,
    T: TopEncode + TopDecode,
{
    fn item_key(&self, index: usize) -> StorageKey<SA> {
        let mut item_key = self.base_key.clone();
        item_key.append_bytes(ITEM_SUFFIX.as_bytes());
        item_key.append_item(&index);
        item_key
    }

    /// Number of items managed by the mapper.
    pub fn len(&self) -> usize {
        self.address.address_storage_get(self.len_key.as_ref())
    }

    /// True if no items present in the mapper.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get item at index from storage.
    /// Index must be valid (1 <= index <= count).
    pub fn get(&self, index: usize) -> T {
        if index == 0 || index > self.len() {
            SA::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_ERR_MSG.as_bytes());
        }
        self.get_unchecked(index)
    }

    /// Get item at index from storage.
    /// There are no restrictions on the index,
    /// calling for an invalid index will simply return the zero-value.
    pub fn get_unchecked(&self, index: usize) -> T {
        self.address
            .address_storage_get(self.item_key(index).as_ref())
    }

    /// Get item at index from storage.
    /// If index is valid (1 <= index <= count), returns value at index,
    /// else calls lambda given as argument.
    /// The lambda only gets called lazily if the index is not valid.
    pub fn get_or_else<F: FnOnce() -> T>(self, index: usize, or_else: F) -> T {
        if index == 0 || index > self.len() {
            or_else()
        } else {
            self.get_unchecked(index)
        }
    }

    /// Checks whether or not there is anything ins storage at index.
    /// Index must be valid (1 <= index <= count).
    pub fn item_is_empty(&self, index: usize) -> bool {
        if index == 0 || index > self.len() {
            SA::error_api_impl().signal_error(INDEX_OUT_OF_RANGE_ERR_MSG.as_bytes());
        }
        self.item_is_empty_unchecked(index)
    }

    /// Checks whether or not there is anything in storage at index.
    /// There are no restrictions on the index,
    /// calling for an invalid index will simply return `true`.
    pub fn item_is_empty_unchecked(&self, index: usize) -> bool {
        self.address
            .address_storage_get_len(self.item_key(index).as_ref())
            == 0
    }

    /// Loads all items from storage and places them in a Vec.
    /// Can easily consume a lot of gas.
    #[cfg(feature = "alloc")]
    pub fn load_as_vec(&self) -> alloc::vec::Vec<T> {
        self.iter().collect()
    }

    /// Provides a forward iterator.
    pub fn iter(&self) -> Iter<'_, SA, T, A> {
        Iter::new(self)
    }
}

impl<SA, T> VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
    fn save_count(&self, new_len: usize) {
        storage_set(self.len_key.as_ref(), &new_len);
    }

    /// Add one item at the end of the list.
    /// Returns the index of the newly inserted item, which is also equal to the new number of elements.
    pub fn push(&mut self, item: &T) -> usize {
        let mut len = self.len();
        len += 1;
        storage_set(self.item_key(len).as_ref(), item);
        self.save_count(len);
        len
    }

    /// Adds multiple items at the end of the list.
    /// Cheaper than multiple `push`-es because the count only gets updated once at the end.
    /// Returns the index of the last inserted item, which is also equal to the new number of elements.
    pub fn extend_from_slice(&mut self, items: &[T]) -> usize {
        let mut len = self.len();
        for item in items {
            len += 1;
            storage_set(self.item_key(len).as_ref(), item);
        }
        self.save_count(len);
        len
    }

    /// Set item at index in storage.
    /// Index must be valid (1 <= index <= count).
    pub fn set(&mut self, index: usize, item: &T) {
        if index == 0 || index > self.len() {
            SA::error_api_impl().signal_error(&b"index out of range"[..]);
        }
        self.set_unchecked(index, item);
    }

    /// Syntactic sugar, to more compactly express a get, update and set in one line.
    /// Takes whatever lies in storage, apples the given closure and saves the final value back to storage.
    /// Propagates the return value of the given function.
    pub fn update<R, F: FnOnce(&mut T) -> R>(&mut self, index: usize, f: F) -> R {
        let mut value = self.get(index);
        let result = f(&mut value);
        self.set(index, &value);
        result
    }

    /// Keeping `set_unchecked` private on purpose, so developers don't write out of index limits by accident.
    fn set_unchecked(&self, index: usize, item: &T) {
        storage_set(self.item_key(index).as_ref(), item);
    }

    /// Clears item at index from storage.
    /// Index must be valid (1 <= index <= count).
    pub fn clear_entry(&self, index: usize) {
        if index == 0 || index > self.len() {
            SA::error_api_impl().signal_error(&b"index out of range"[..]);
        }
        self.clear_entry_unchecked(index)
    }

    /// Clears item at index from storage.
    /// There are no restrictions on the index,
    /// calling for an invalid index will simply do nothing.
    pub fn clear_entry_unchecked(&self, index: usize) {
        storage_clear(self.item_key(index).as_ref());
    }

    /// Clears item at index from storage by swap remove
    /// last item takes the index of the item to remove
    /// and we remove the last index.
    pub fn swap_remove(&mut self, index: usize) {
        let _ = self.swap_remove_and_get_old_last(index);
    }

    pub(crate) fn swap_remove_and_get_old_last(&mut self, index: usize) -> Option<T> {
        let last_item_index = self.len();
        if index == 0 || index > last_item_index {
            SA::error_api_impl().signal_error(&b"index out of range"[..]);
        }

        let mut last_item_as_option = Option::None;
        if index != last_item_index {
            let last_item = self.get(last_item_index);
            self.set(index, &last_item);
            last_item_as_option = Some(last_item);
        }
        self.clear_entry(last_item_index);
        self.save_count(last_item_index - 1);
        last_item_as_option
    }

    /// Deletes all contents form storage and sets count to 0.
    /// Can easily consume a lot of gas.
    pub fn clear(&mut self) {
        let len = self.len();
        for i in 1..=len {
            storage_clear(self.item_key(i).as_ref());
        }
        self.save_count(0);
    }
}

impl<'a, SA, T, A> IntoIterator for &'a VecMapper<SA, T, A>
where
    SA: StorageMapperApi,
    A: StorageAddress<SA>,
    T: TopEncode + TopDecode + 'static,
{
    type Item = T;

    type IntoIter = Iter<'a, SA, T, A>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

/// An iterator over the elements of a `VecMapper`.
///
/// This `struct` is created by [`VecMapper::iter()`]. See its
/// documentation for more.
pub struct Iter<'a, SA, T, A>
where
    SA: StorageMapperApi,
    A: StorageAddress<SA>,
    T: TopEncode + TopDecode + 'static,
{
    index: usize,
    len: usize,
    vec: &'a VecMapper<SA, T, A>,
}

impl<'a, SA, T, A> Iter<'a, SA, T, A>
where
    SA: StorageMapperApi,
    A: StorageAddress<SA>,
    T: TopEncode + TopDecode + 'static,
{
    fn new(vec: &'a VecMapper<SA, T, A>) -> Iter<'a, SA, T, A> {
        Iter {
            index: 1,
            len: vec.len(),
            vec,
        }
    }
}

impl<SA, T, A> Iterator for Iter<'_, SA, T, A>
where
    SA: StorageMapperApi,
    A: StorageAddress<SA>,
    T: TopEncode + TopDecode + 'static,
{
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        let current_index = self.index;
        if current_index > self.len {
            return None;
        }
        self.index += 1;
        Some(self.vec.get_unchecked(current_index))
    }
}

/// Behaves like a MultiResultVec when an endpoint result.
impl<SA, T> TopEncodeMulti for VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
    fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
    where
        O: TopEncodeMultiOutput,
        H: EncodeErrorHandler,
    {
        multi_encode_iter_or_handle_err(self.iter(), output, h)
    }
}

impl<SA, T> TypeAbiFrom<VecMapper<SA, T, CurrentStorage>> for MultiValueEncoded<SA, T>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
}

impl<SA, T> TypeAbiFrom<Self> for VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode,
{
}

/// Behaves like a MultiResultVec when an endpoint result.
impl<SA, T> TypeAbi for VecMapper<SA, T, CurrentStorage>
where
    SA: StorageMapperApi,
    T: TopEncode + TopDecode + TypeAbi,
{
    type Unmanaged = Self;

    fn type_name() -> TypeName {
        crate::abi::type_name_variadic::<T>()
    }

    fn type_name_rust() -> TypeName {
        crate::abi::type_name_multi_value_encoded::<T>()
    }

    fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
        T::provide_type_descriptions(accumulator);
    }

    fn is_variadic() -> bool {
        true
    }
}
