use crate::{
    identifier::{SenderId, TransactionId},
    pool::size::SizeTracker,
    PoolTransaction, SubPoolLimit, ValidPoolTransaction, TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER,
};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use std::{
    cmp::Ordering,
    collections::{hash_map::Entry, BTreeMap, BTreeSet},
    ops::{Bound::Unbounded, Deref},
    sync::Arc,
};

/// A pool of transactions that are currently parked and are waiting for external changes (e.g.
/// basefee, ancestor transactions, balance) that eventually move the transaction into the pending
/// pool.
///
/// Note: This type is generic over [`ParkedPool`] which enforces that the underlying transaction
/// type is [`ValidPoolTransaction`] wrapped in an [Arc].
#[derive(Debug, Clone)]
pub struct ParkedPool<T: ParkedOrd> {
    /// Keeps track of transactions inserted in the pool.
    ///
    /// This way we can determine when transactions were submitted to the pool.
    submission_id: u64,
    /// _All_ Transactions that are currently inside the pool grouped by their identifier.
    by_id: BTreeMap<TransactionId, ParkedPoolTransaction<T>>,
    /// Keeps track of last submission id for each sender.
    ///
    /// This are sorted in reverse order, so the last (highest) submission id is first, and the
    /// lowest (oldest) is the last.
    last_sender_submission: BTreeSet<SubmissionSenderId>,
    /// Keeps track of the number of transactions in the pool by the sender and the last submission
    /// id.
    sender_transaction_count: FxHashMap<SenderId, SenderTransactionCount>,
    /// Keeps track of the size of this pool.
    ///
    /// See also [`reth_primitives_traits::InMemorySize::size`].
    size_of: SizeTracker,
}

// === impl ParkedPool ===

impl<T: ParkedOrd> ParkedPool<T> {
    /// Adds a new transactions to the pending queue.
    pub fn add_transaction(&mut self, tx: Arc<ValidPoolTransaction<T::Transaction>>) {
        let id = *tx.id();
        debug_assert!(
            !self.contains(&id),
            "transaction already included {:?}",
            self.get(&id).unwrap().transaction.transaction
        );
        let submission_id = self.next_id();

        // keep track of size
        self.size_of += tx.size();

        // update or create sender entry
        self.add_sender_count(tx.sender_id(), submission_id);
        let transaction = ParkedPoolTransaction { submission_id, transaction: tx.into() };

        self.by_id.insert(id, transaction);
    }

    /// Increments the count of transactions for the given sender and updates the tracked submission
    /// id.
    fn add_sender_count(&mut self, sender: SenderId, submission_id: u64) {
        match self.sender_transaction_count.entry(sender) {
            Entry::Occupied(mut entry) => {
                let value = entry.get_mut();
                // remove the __currently__ tracked submission id
                self.last_sender_submission
                    .remove(&SubmissionSenderId::new(sender, value.last_submission_id));

                value.count += 1;
                value.last_submission_id = submission_id;
            }
            Entry::Vacant(entry) => {
                entry
                    .insert(SenderTransactionCount { count: 1, last_submission_id: submission_id });
            }
        }
        // insert a new entry
        self.last_sender_submission.insert(SubmissionSenderId::new(sender, submission_id));
    }

    /// Decrements the count of transactions for the given sender.
    ///
    /// If the count reaches zero, the sender is removed from the map.
    ///
    /// Note: this does not update the tracked submission id for the sender, because we're only
    /// interested in the __last__ submission id when truncating the pool.
    fn remove_sender_count(&mut self, sender_id: SenderId) {
        let removed_sender = match self.sender_transaction_count.entry(sender_id) {
            Entry::Occupied(mut entry) => {
                let value = entry.get_mut();
                value.count -= 1;
                if value.count == 0 {
                    entry.remove()
                } else {
                    return
                }
            }
            Entry::Vacant(_) => {
                // This should never happen because the bisection between the two maps
                unreachable!("sender count not found {:?}", sender_id);
            }
        };

        // all transactions for this sender have been removed
        assert!(
            self.last_sender_submission
                .remove(&SubmissionSenderId::new(sender_id, removed_sender.last_submission_id)),
            "last sender transaction not found {sender_id:?}"
        );
    }

    /// Returns an iterator over all transactions in the pool
    pub(crate) fn all(
        &self,
    ) -> impl ExactSizeIterator<Item = Arc<ValidPoolTransaction<T::Transaction>>> + '_ {
        self.by_id.values().map(|tx| tx.transaction.clone().into())
    }

    /// Removes the transaction from the pool
    pub(crate) fn remove_transaction(
        &mut self,
        id: &TransactionId,
    ) -> Option<Arc<ValidPoolTransaction<T::Transaction>>> {
        // remove from queues
        let tx = self.by_id.remove(id)?;
        self.remove_sender_count(tx.transaction.sender_id());

        // keep track of size
        self.size_of -= tx.transaction.size();

        Some(tx.transaction.into())
    }

    /// Retrieves transactions by sender, using `SmallVec` to efficiently handle up to
    /// `TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER` transactions.
    pub(crate) fn get_txs_by_sender(
        &self,
        sender: SenderId,
    ) -> SmallVec<[TransactionId; TXPOOL_MAX_ACCOUNT_SLOTS_PER_SENDER]> {
        self.by_id
            .range((sender.start_bound(), Unbounded))
            .take_while(move |(other, _)| sender == other.sender)
            .map(|(tx_id, _)| *tx_id)
            .collect()
    }

    /// Returns all transactions for the given sender, using a `BTree` range query.
    pub(crate) fn txs_by_sender(
        &self,
        sender: SenderId,
    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
        self.by_id
            .range((sender.start_bound(), Unbounded))
            .take_while(move |(other, _)| sender == other.sender)
            .map(|(_, tx)| Arc::clone(&tx.transaction))
            .collect()
    }

    #[cfg(test)]
    pub(crate) fn get_senders_by_submission_id(
        &self,
    ) -> impl Iterator<Item = SubmissionSenderId> + '_ {
        self.last_sender_submission.iter().copied()
    }

    /// Truncates the pool by removing transactions, until the given [`SubPoolLimit`] has been met.
    ///
    /// This is done by first ordering senders by the last time they have submitted a transaction
    ///
    /// Uses sender ids sorted by each sender's last submission id. Senders with older last
    /// submission ids are first. Note that _last_ submission ids are the newest submission id for
    /// that sender, so this sorts senders by the last time they submitted a transaction in
    /// descending order. Senders that have least recently submitted a transaction are first.
    ///
    /// Then, for each sender, all transactions for that sender are removed, until the pool limits
    /// have been met.
    ///
    /// Any removed transactions are returned.
    pub fn truncate_pool(
        &mut self,
        limit: SubPoolLimit,
    ) -> Vec<Arc<ValidPoolTransaction<T::Transaction>>> {
        if !self.exceeds(&limit) {
            // if we are below the limits, we don't need to drop anything
            return Vec::new()
        }

        let mut removed = Vec::with_capacity(limit.tx_excess(self.len()).unwrap_or(1));

        while !self.last_sender_submission.is_empty() && limit.is_exceeded(self.len(), self.size())
        {
            // NOTE: This will not panic due to `!last_sender_transaction.is_empty()`
            let sender_id = self.last_sender_submission.last().unwrap().sender_id;

            // Drop transactions from this sender until the pool is under limits
            while let Some((tx_id, _)) = self.by_id.range(sender_id.range()).next_back() {
                let tx_id = *tx_id;
                if let Some(tx) = self.remove_transaction(&tx_id) {
                    removed.push(tx);
                }

                if !self.exceeds(&limit) {
                    break
                }
            }
        }

        removed
    }

    const fn next_id(&mut self) -> u64 {
        let id = self.submission_id;
        self.submission_id = self.submission_id.wrapping_add(1);
        id
    }

    /// The reported size of all transactions in this pool.
    pub(crate) fn size(&self) -> usize {
        self.size_of.into()
    }

    /// Number of transactions in the entire pool
    pub(crate) fn len(&self) -> usize {
        self.by_id.len()
    }

    /// Returns true if the pool exceeds the given limit
    #[inline]
    pub(crate) fn exceeds(&self, limit: &SubPoolLimit) -> bool {
        limit.is_exceeded(self.len(), self.size())
    }

    /// Returns whether the pool is empty
    #[cfg(test)]
    pub(crate) fn is_empty(&self) -> bool {
        self.by_id.is_empty()
    }

    /// Returns `true` if the transaction with the given id is already included in this pool.
    pub(crate) fn contains(&self, id: &TransactionId) -> bool {
        self.by_id.contains_key(id)
    }

    /// Retrieves a transaction with the given ID from the pool, if it exists.
    fn get(&self, id: &TransactionId) -> Option<&ParkedPoolTransaction<T>> {
        self.by_id.get(id)
    }

    /// Asserts that all subpool invariants
    #[cfg(any(test, feature = "test-utils"))]
    pub(crate) fn assert_invariants(&self) {
        assert_eq!(
            self.last_sender_submission.len(),
            self.sender_transaction_count.len(),
            "last_sender_submission.len() != sender_transaction_count.len()"
        );
    }
}

impl<T: PoolTransaction> ParkedPool<BasefeeOrd<T>> {
    /// Returns all transactions that satisfy the given basefee.
    ///
    /// Note: this does _not_ remove the transactions
    pub(crate) fn satisfy_base_fee_transactions(
        &self,
        basefee: u64,
    ) -> Vec<Arc<ValidPoolTransaction<T>>> {
        let mut txs = Vec::new();
        self.satisfy_base_fee_ids(basefee as u128, |tx| {
            txs.push(tx.clone());
        });
        txs
    }

    /// Returns all transactions that satisfy the given basefee.
    fn satisfy_base_fee_ids<F>(&self, basefee: u128, mut tx_handler: F)
    where
        F: FnMut(&Arc<ValidPoolTransaction<T>>),
    {
        let mut iter = self.by_id.iter().peekable();

        while let Some((id, tx)) = iter.next() {
            if tx.transaction.max_fee_per_gas() < basefee {
                // still parked -> skip descendant transactions
                'this: while let Some((peek, _)) = iter.peek() {
                    if peek.sender != id.sender {
                        break 'this
                    }
                    iter.next();
                }
            } else {
                tx_handler(&tx.transaction);
            }
        }
    }

    /// Removes all transactions from this subpool that can afford the given basefee,
    /// invoking the provided handler for each transaction as it is removed.
    ///
    /// This method enforces the basefee constraint by identifying transactions that now
    /// satisfy the basefee requirement (typically after a basefee decrease) and processing
    /// them via the provided transaction handler closure.
    ///
    /// Respects per-sender nonce ordering: if the lowest-nonce transaction for a sender
    /// still cannot afford the basefee, higher-nonce transactions from that sender are skipped.
    ///
    /// Note: the transactions are not returned in a particular order.
    pub(crate) fn enforce_basefee_with<F>(&mut self, basefee: u64, mut tx_handler: F)
    where
        F: FnMut(Arc<ValidPoolTransaction<T>>),
    {
        let mut to_remove = Vec::new();
        self.satisfy_base_fee_ids(basefee as u128, |tx| {
            to_remove.push(*tx.id());
        });

        for id in to_remove {
            if let Some(tx) = self.remove_transaction(&id) {
                tx_handler(tx);
            }
        }
    }

    /// Removes all transactions and their dependent transaction from the subpool that no longer
    /// satisfy the given basefee.
    ///
    /// Legacy method maintained for compatibility with read-only queries.
    /// For basefee enforcement, prefer `enforce_basefee_with` for better performance.
    ///
    /// Note: the transactions are not returned in a particular order.
    #[cfg(test)]
    pub(crate) fn enforce_basefee(&mut self, basefee: u64) -> Vec<Arc<ValidPoolTransaction<T>>> {
        let mut removed = Vec::new();
        self.enforce_basefee_with(basefee, |tx| {
            removed.push(tx);
        });
        removed
    }
}

impl<T: ParkedOrd> Default for ParkedPool<T> {
    fn default() -> Self {
        Self {
            submission_id: 0,
            by_id: Default::default(),
            last_sender_submission: Default::default(),
            sender_transaction_count: Default::default(),
            size_of: Default::default(),
        }
    }
}

/// Keeps track of the number of transactions and the latest submission id for each sender.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct SenderTransactionCount {
    count: u64,
    last_submission_id: u64,
}

/// Represents a transaction in this pool.
#[derive(Debug)]
struct ParkedPoolTransaction<T: ParkedOrd> {
    /// Identifier that tags when transaction was submitted in the pool.
    submission_id: u64,
    /// Actual transaction.
    transaction: T,
}

impl<T: ParkedOrd> Clone for ParkedPoolTransaction<T> {
    fn clone(&self) -> Self {
        Self { submission_id: self.submission_id, transaction: self.transaction.clone() }
    }
}

impl<T: ParkedOrd> Eq for ParkedPoolTransaction<T> {}

impl<T: ParkedOrd> PartialEq<Self> for ParkedPoolTransaction<T> {
    fn eq(&self, other: &Self) -> bool {
        self.cmp(other) == Ordering::Equal
    }
}

impl<T: ParkedOrd> PartialOrd<Self> for ParkedPoolTransaction<T> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: ParkedOrd> Ord for ParkedPoolTransaction<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        // This compares by the transactions first, and only if two tx are equal this compares
        // the unique `submission_id`.
        // "better" transactions are Greater
        self.transaction
            .cmp(&other.transaction)
            .then_with(|| other.submission_id.cmp(&self.submission_id))
    }
}

/// Includes a [`SenderId`] and `submission_id`. This is used to sort senders by their last
/// submission id.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub(crate) struct SubmissionSenderId {
    /// The sender id
    pub(crate) sender_id: SenderId,
    /// The submission id
    pub(crate) submission_id: u64,
}

impl SubmissionSenderId {
    /// Creates a new [`SubmissionSenderId`] based on the [`SenderId`] and `submission_id`.
    const fn new(sender_id: SenderId, submission_id: u64) -> Self {
        Self { sender_id, submission_id }
    }
}

impl Ord for SubmissionSenderId {
    fn cmp(&self, other: &Self) -> Ordering {
        // Reverse ordering for `submission_id`
        other.submission_id.cmp(&self.submission_id)
    }
}

impl PartialOrd for SubmissionSenderId {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

/// Helper trait used for custom `Ord` wrappers around a transaction.
///
/// This is effectively a wrapper for `Arc<ValidPoolTransaction>` with custom `Ord` implementation.
pub trait ParkedOrd:
    Ord
    + Clone
    + From<Arc<ValidPoolTransaction<Self::Transaction>>>
    + Into<Arc<ValidPoolTransaction<Self::Transaction>>>
    + Deref<Target = Arc<ValidPoolTransaction<Self::Transaction>>>
{
    /// The wrapper transaction type.
    type Transaction: PoolTransaction;
}

/// Helper macro to implement necessary conversions for `ParkedOrd` trait
macro_rules! impl_ord_wrapper {
    ($name:ident) => {
        impl<T: PoolTransaction> Clone for $name<T> {
            fn clone(&self) -> Self {
                Self(self.0.clone())
            }
        }

        impl<T: PoolTransaction> Eq for $name<T> {}

        impl<T: PoolTransaction> PartialEq<Self> for $name<T> {
            fn eq(&self, other: &Self) -> bool {
                self.cmp(other) == Ordering::Equal
            }
        }

        impl<T: PoolTransaction> PartialOrd<Self> for $name<T> {
            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
                Some(self.cmp(other))
            }
        }
        impl<T: PoolTransaction> Deref for $name<T> {
            type Target = Arc<ValidPoolTransaction<T>>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl<T: PoolTransaction> ParkedOrd for $name<T> {
            type Transaction = T;
        }

        impl<T: PoolTransaction> From<Arc<ValidPoolTransaction<T>>> for $name<T> {
            fn from(value: Arc<ValidPoolTransaction<T>>) -> Self {
                Self(value)
            }
        }

        impl<T: PoolTransaction> From<$name<T>> for Arc<ValidPoolTransaction<T>> {
            fn from(value: $name<T>) -> Arc<ValidPoolTransaction<T>> {
                value.0
            }
        }
    };
}

/// A new type wrapper for [`ValidPoolTransaction`]
///
/// This sorts transactions by their base fee.
///
/// Caution: This assumes all transaction in the `BaseFee` sub-pool have a fee value.
#[derive(Debug)]
pub struct BasefeeOrd<T: PoolTransaction>(Arc<ValidPoolTransaction<T>>);

impl_ord_wrapper!(BasefeeOrd);

impl<T: PoolTransaction> Ord for BasefeeOrd<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.transaction.max_fee_per_gas().cmp(&other.0.transaction.max_fee_per_gas())
    }
}

/// A new type wrapper for [`ValidPoolTransaction`]
///
/// This sorts transactions by their distance.
///
/// `Queued` transactions are transactions that are currently blocked by other parked (basefee,
/// queued) or missing transactions.
///
/// The primary order function always compares the transaction costs first. In case these
/// are equal, it compares the timestamps when the transactions were created.
#[derive(Debug)]
pub struct QueuedOrd<T: PoolTransaction>(Arc<ValidPoolTransaction<T>>);

impl_ord_wrapper!(QueuedOrd);

impl<T: PoolTransaction> Ord for QueuedOrd<T> {
    fn cmp(&self, other: &Self) -> Ordering {
        // Higher fee is better
        self.max_fee_per_gas().cmp(&other.max_fee_per_gas()).then_with(||
            // Lower timestamp is better
            other.timestamp.cmp(&self.timestamp))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::{MockTransaction, MockTransactionFactory, MockTransactionSet};
    use alloy_consensus::{Transaction, TxType};
    use alloy_primitives::address;
    use std::collections::HashSet;

    #[test]
    fn test_enforce_parked_basefee() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
        pool.add_transaction(tx.clone());

        assert!(pool.contains(tx.id()));
        assert_eq!(pool.len(), 1);

        let removed = pool.enforce_basefee(u64::MAX);
        assert!(removed.is_empty());

        let removed = pool.enforce_basefee((tx.max_fee_per_gas() - 1) as u64);
        assert_eq!(removed.len(), 1);
        assert!(pool.is_empty());
    }

    #[test]
    fn test_enforce_parked_basefee_descendant() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        let t = MockTransaction::eip1559().inc_price_by(10);
        let root_tx = f.validated_arc(t.clone());
        pool.add_transaction(root_tx.clone());

        let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
        pool.add_transaction(descendant_tx.clone());

        assert!(pool.contains(root_tx.id()));
        assert!(pool.contains(descendant_tx.id()));
        assert_eq!(pool.len(), 2);

        let removed = pool.enforce_basefee(u64::MAX);
        assert!(removed.is_empty());
        assert_eq!(pool.len(), 2);
        // two dependent tx in the pool with decreasing fee

        // remove root transaction via descendant tx fee
        let removed = pool.enforce_basefee(descendant_tx.max_fee_per_gas() as u64);
        assert_eq!(removed.len(), 2);
        assert!(pool.is_empty());
    }

    #[test]
    fn test_enforce_parked_basefee_removes_only_eligible_ancestor() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        let t = MockTransaction::eip1559().inc_price_by(10);

        let root_tx = f.validated_arc(t.clone());
        pool.add_transaction(root_tx.clone());

        let descendant_tx = f.validated_arc(t.inc_nonce().decr_price());
        pool.add_transaction(descendant_tx.clone());

        let removed = pool.enforce_basefee(root_tx.max_fee_per_gas() as u64);

        assert_eq!(removed.len(), 1);
        assert_eq!(removed[0].id(), root_tx.id());
        assert_eq!(pool.len(), 1);
        assert!(!pool.contains(root_tx.id()));
        assert!(pool.contains(descendant_tx.id()));
    }

    #[test]
    fn truncate_parked_by_submission_id() {
        // this test ensures that we evict from the pending pool by sender
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        let a_sender = address!("0x000000000000000000000000000000000000000a");
        let b_sender = address!("0x000000000000000000000000000000000000000b");
        let c_sender = address!("0x000000000000000000000000000000000000000c");
        let d_sender = address!("0x000000000000000000000000000000000000000d");

        // create a chain of transactions by sender A, B, C
        let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
        let a = tx_set.clone().into_vec();

        let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
        tx_set.extend(b.clone());

        // C has the same number of txs as B
        let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
        tx_set.extend(c.clone());

        let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
        tx_set.extend(d.clone());

        let all_txs = tx_set.into_vec();

        // just construct a list of all txs to add
        let expected_parked = vec![c[0].clone(), c[1].clone(), c[2].clone(), d[0].clone()]
            .into_iter()
            .map(|tx| (tx.sender(), tx.nonce()))
            .collect::<HashSet<_>>();

        // we expect the truncate operation to go through the senders with the most txs, removing
        // txs based on when they were submitted, removing the oldest txs first, until the pool is
        // not over the limit
        let expected_removed = vec![
            a[0].clone(),
            a[1].clone(),
            a[2].clone(),
            a[3].clone(),
            b[0].clone(),
            b[1].clone(),
            b[2].clone(),
        ]
        .into_iter()
        .map(|tx| (tx.sender(), tx.nonce()))
        .collect::<HashSet<_>>();

        // add all the transactions to the pool
        for tx in all_txs {
            pool.add_transaction(f.validated_arc(tx));
        }

        // we should end up with the most recently submitted transactions
        let pool_limit = SubPoolLimit { max_txs: 4, max_size: usize::MAX };

        // truncate the pool
        let removed = pool.truncate_pool(pool_limit);
        assert_eq!(removed.len(), expected_removed.len());

        // get the inner txs from the removed txs
        let removed =
            removed.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
        assert_eq!(removed, expected_removed);

        // get the parked pool
        let parked = pool.all().collect::<Vec<_>>();
        assert_eq!(parked.len(), expected_parked.len());

        // get the inner txs from the parked txs
        let parked = parked.into_iter().map(|tx| (tx.sender(), tx.nonce())).collect::<HashSet<_>>();
        assert_eq!(parked, expected_parked);
    }

    #[test]
    fn test_truncate_parked_with_large_tx() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        let default_limits = SubPoolLimit::default();

        // create a chain of transactions by sender A
        // make sure they are all one over half the limit
        let a_sender = address!("0x000000000000000000000000000000000000000a");

        // 2 txs, that should put the pool over the size limit but not max txs
        let a_txs = MockTransactionSet::dependent(a_sender, 0, 2, TxType::Eip1559)
            .into_iter()
            .map(|mut tx| {
                tx.set_size(default_limits.max_size / 2 + 1);
                tx
            })
            .collect::<Vec<_>>();

        // add all the transactions to the pool
        for tx in a_txs {
            pool.add_transaction(f.validated_arc(tx));
        }

        // truncate the pool, it should remove at least one transaction
        let removed = pool.truncate_pool(default_limits);
        assert_eq!(removed.len(), 1);
    }

    #[test]
    fn test_senders_by_submission_id() {
        // this test ensures that we evict from the pending pool by sender
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        let a_sender = address!("0x000000000000000000000000000000000000000a");
        let b_sender = address!("0x000000000000000000000000000000000000000b");
        let c_sender = address!("0x000000000000000000000000000000000000000c");
        let d_sender = address!("0x000000000000000000000000000000000000000d");

        // create a chain of transactions by sender A, B, C
        let mut tx_set = MockTransactionSet::dependent(a_sender, 0, 4, TxType::Eip1559);
        let a = tx_set.clone().into_vec();

        let b = MockTransactionSet::dependent(b_sender, 0, 3, TxType::Eip1559).into_vec();
        tx_set.extend(b.clone());

        // C has the same number of txs as B
        let c = MockTransactionSet::dependent(c_sender, 0, 3, TxType::Eip1559).into_vec();
        tx_set.extend(c.clone());

        let d = MockTransactionSet::dependent(d_sender, 0, 1, TxType::Eip1559).into_vec();
        tx_set.extend(d.clone());

        let all_txs = tx_set.into_vec();

        // add all the transactions to the pool
        for tx in all_txs {
            pool.add_transaction(f.validated_arc(tx));
        }

        // get senders by submission id - a4, b3, c3, d1, reversed
        let senders = pool.get_senders_by_submission_id().map(|s| s.sender_id).collect::<Vec<_>>();
        assert_eq!(senders.len(), 4);
        let expected_senders = vec![d_sender, c_sender, b_sender, a_sender]
            .into_iter()
            .map(|s| f.ids.sender_id(&s).unwrap())
            .collect::<Vec<_>>();
        assert_eq!(senders, expected_senders);

        // manually order the txs
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        let all_txs = vec![d[0].clone(), b[0].clone(), c[0].clone(), a[0].clone()];

        // add all the transactions to the pool
        for tx in all_txs {
            pool.add_transaction(f.validated_arc(tx));
        }

        let senders = pool.get_senders_by_submission_id().map(|s| s.sender_id).collect::<Vec<_>>();
        assert_eq!(senders.len(), 4);
        let expected_senders = vec![a_sender, c_sender, b_sender, d_sender]
            .into_iter()
            .map(|s| f.ids.sender_id(&s).unwrap())
            .collect::<Vec<_>>();
        assert_eq!(senders, expected_senders);
    }

    #[test]
    fn test_add_sender_count_new_sender() {
        // Initialize a mock transaction factory
        let mut f = MockTransactionFactory::default();
        // Create an empty transaction pool
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        // Generate a validated transaction and add it to the pool
        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
        pool.add_transaction(tx);

        // Define a new sender ID and submission ID
        let sender: SenderId = 11.into();
        let submission_id = 1;

        // Add the sender count to the pool
        pool.add_sender_count(sender, submission_id);

        // Assert that the sender transaction count is updated correctly
        assert_eq!(pool.sender_transaction_count.len(), 2);
        let sender_info = pool.sender_transaction_count.get(&sender).unwrap();
        assert_eq!(sender_info.count, 1);
        assert_eq!(sender_info.last_submission_id, submission_id);

        // Assert that the last sender submission is updated correctly
        assert_eq!(pool.last_sender_submission.len(), 2);
        let submission_info = pool.last_sender_submission.iter().next().unwrap();
        assert_eq!(submission_info.sender_id, sender);
        assert_eq!(submission_info.submission_id, submission_id);
    }

    #[test]
    fn test_add_sender_count_existing_sender() {
        // Initialize a mock transaction factory
        let mut f = MockTransactionFactory::default();
        // Create an empty transaction pool
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        // Generate a validated transaction and add it to the pool
        let tx = f.validated_arc(MockTransaction::eip1559().inc_price());
        pool.add_transaction(tx);

        // Define a sender ID and initial submission ID
        let sender: SenderId = 11.into();
        let initial_submission_id = 1;

        // Add the sender count to the pool with the initial submission ID
        pool.add_sender_count(sender, initial_submission_id);

        // Define a new submission ID
        let new_submission_id = 2;
        // Add the sender count to the pool with the new submission ID
        pool.add_sender_count(sender, new_submission_id);

        // Assert that the sender transaction count is updated correctly
        assert_eq!(pool.sender_transaction_count.len(), 2);
        let sender_info = pool.sender_transaction_count.get(&sender).unwrap();
        assert_eq!(sender_info.count, 2);
        assert_eq!(sender_info.last_submission_id, new_submission_id);

        // Assert that the last sender submission is updated correctly
        assert_eq!(pool.last_sender_submission.len(), 2);
        let submission_info = pool.last_sender_submission.iter().next().unwrap();
        assert_eq!(submission_info.sender_id, sender);
        assert_eq!(submission_info.submission_id, new_submission_id);
    }

    #[test]
    fn test_add_sender_count_multiple_senders() {
        // Initialize a mock transaction factory
        let mut f = MockTransactionFactory::default();
        // Create an empty transaction pool
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        // Generate two validated transactions and add them to the pool
        let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
        let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price());
        pool.add_transaction(tx1);
        pool.add_transaction(tx2);

        // Define two different sender IDs and their corresponding submission IDs
        let sender1: SenderId = 11.into();
        let sender2: SenderId = 22.into();

        // Add the sender counts to the pool
        pool.add_sender_count(sender1, 1);
        pool.add_sender_count(sender2, 2);

        // Assert that the sender transaction counts are updated correctly
        assert_eq!(pool.sender_transaction_count.len(), 4);

        let sender1_info = pool.sender_transaction_count.get(&sender1).unwrap();
        assert_eq!(sender1_info.count, 1);
        assert_eq!(sender1_info.last_submission_id, 1);

        let sender2_info = pool.sender_transaction_count.get(&sender2).unwrap();
        assert_eq!(sender2_info.count, 1);
        assert_eq!(sender2_info.last_submission_id, 2);

        // Assert that the last sender submission is updated correctly
        assert_eq!(pool.last_sender_submission.len(), 3);

        // Verify that sender 1 is not in the last sender submission
        let submission_info1 =
            pool.last_sender_submission.iter().find(|info| info.sender_id == sender1);
        assert!(submission_info1.is_none());

        // Verify that sender 2 is in the last sender submission
        let submission_info2 =
            pool.last_sender_submission.iter().find(|info| info.sender_id == sender2).unwrap();
        assert_eq!(submission_info2.sender_id, sender2);
        assert_eq!(submission_info2.submission_id, 2);
    }

    #[test]
    fn test_remove_sender_count() {
        // Initialize a mock transaction factory
        let mut f = MockTransactionFactory::default();
        // Create an empty transaction pool
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();
        // Generate two validated transactions and add them to the pool
        let tx1 = f.validated_arc(MockTransaction::eip1559().inc_price());
        let tx2 = f.validated_arc(MockTransaction::eip1559().inc_price());
        pool.add_transaction(tx1);
        pool.add_transaction(tx2);

        // Define two different sender IDs and their corresponding submission IDs
        let sender1: SenderId = 11.into();
        let sender2: SenderId = 22.into();

        // Add the sender counts to the pool
        pool.add_sender_count(sender1, 1);

        // We add sender 2 multiple times to test the removal of sender counts
        pool.add_sender_count(sender2, 2);
        pool.add_sender_count(sender2, 3);

        // Before removing the sender count we should have 4 sender transaction counts
        assert_eq!(pool.sender_transaction_count.len(), 4);
        assert!(pool.sender_transaction_count.contains_key(&sender1));

        // We should have 1 sender transaction count for sender 1 before removing the sender count
        assert_eq!(pool.sender_transaction_count.get(&sender1).unwrap().count, 1);

        // Remove the sender count for sender 1
        pool.remove_sender_count(sender1);

        // After removing the sender count we should have 3 sender transaction counts remaining
        assert_eq!(pool.sender_transaction_count.len(), 3);
        assert!(!pool.sender_transaction_count.contains_key(&sender1));

        // Check the sender transaction count for sender 2 before removing the sender count
        assert_eq!(
            *pool.sender_transaction_count.get(&sender2).unwrap(),
            SenderTransactionCount { count: 2, last_submission_id: 3 }
        );

        // Remove the sender count for sender 2
        pool.remove_sender_count(sender2);

        // After removing the sender count for sender 2, we still have 3 sender transaction counts
        // remaining.
        //
        // This is because we added sender 2 multiple times and we only removed the last submission.
        assert_eq!(pool.sender_transaction_count.len(), 3);
        assert!(pool.sender_transaction_count.contains_key(&sender2));

        // Sender transaction count for sender 2 should be updated correctly
        assert_eq!(
            *pool.sender_transaction_count.get(&sender2).unwrap(),
            SenderTransactionCount { count: 1, last_submission_id: 3 }
        );
    }

    #[test]
    fn test_pool_size() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Create a transaction with a specific size and add it to the pool
        let tx = f.validated_arc(MockTransaction::eip1559().set_size(1024).clone());
        pool.add_transaction(tx);

        // Assert that the reported size of the pool is correct
        assert_eq!(pool.size(), 1024);
    }

    #[test]
    fn test_pool_len() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Initially, the pool should have zero transactions
        assert_eq!(pool.len(), 0);

        // Add a transaction to the pool and check the length
        let tx = f.validated_arc(MockTransaction::eip1559());
        pool.add_transaction(tx);
        assert_eq!(pool.len(), 1);
    }

    #[test]
    fn test_pool_contains() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Create a transaction and get its ID
        let tx = f.validated_arc(MockTransaction::eip1559());
        let tx_id = *tx.id();

        // Before adding, the transaction should not be in the pool
        assert!(!pool.contains(&tx_id));

        // After adding, the transaction should be present in the pool
        pool.add_transaction(tx);
        assert!(pool.contains(&tx_id));
    }

    #[test]
    fn test_get_transaction() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add a transaction to the pool and get its ID
        let tx = f.validated_arc(MockTransaction::eip1559());
        let tx_id = *tx.id();
        pool.add_transaction(tx.clone());

        // Retrieve the transaction using `get()` and assert it matches the added transaction
        let retrieved = pool.get(&tx_id).expect("Transaction should exist in the pool");
        assert_eq!(retrieved.transaction.id(), tx.id());
    }

    #[test]
    fn test_all_transactions() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add two transactions to the pool
        let tx1 = f.validated_arc(MockTransaction::eip1559());
        let tx2 = f.validated_arc(MockTransaction::eip1559());
        pool.add_transaction(tx1.clone());
        pool.add_transaction(tx2.clone());

        // Collect all transaction IDs from the pool
        let all_txs: Vec<_> = pool.all().map(|tx| *tx.id()).collect();
        assert_eq!(all_txs.len(), 2);

        // Check that the IDs of both transactions are present
        assert!(all_txs.contains(tx1.id()));
        assert!(all_txs.contains(tx2.id()));
    }

    #[test]
    fn test_truncate_pool_edge_case() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add two transactions to the pool
        let tx1 = f.validated_arc(MockTransaction::eip1559());
        let tx2 = f.validated_arc(MockTransaction::eip1559());
        pool.add_transaction(tx1);
        pool.add_transaction(tx2);

        // Set a limit that matches the current number of transactions
        let limit = SubPoolLimit { max_txs: 2, max_size: usize::MAX };
        let removed = pool.truncate_pool(limit);

        // No transactions should be removed
        assert!(removed.is_empty());

        // Set a stricter limit that requires truncating one transaction
        let limit = SubPoolLimit { max_txs: 1, max_size: usize::MAX };
        let removed = pool.truncate_pool(limit);

        // One transaction should be removed, and the pool should have one left
        assert_eq!(removed.len(), 1);
        assert_eq!(pool.len(), 1);
    }

    #[test]
    fn test_satisfy_base_fee_transactions() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add two transactions with different max fees
        let tx1 = f.validated_arc(MockTransaction::eip1559().set_max_fee(100).clone());
        let tx2 = f.validated_arc(MockTransaction::eip1559().set_max_fee(200).clone());
        pool.add_transaction(tx1);
        pool.add_transaction(tx2.clone());

        // Check that only the second transaction satisfies the base fee requirement
        let satisfied = pool.satisfy_base_fee_transactions(150);
        assert_eq!(satisfied.len(), 1);
        assert_eq!(satisfied[0].id(), tx2.id())
    }

    #[test]
    fn test_remove_transaction() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add a transaction to the pool and get its ID
        let tx = f.validated_arc(MockTransaction::eip1559());
        let tx_id = *tx.id();
        pool.add_transaction(tx);

        // Ensure the transaction is in the pool before removal
        assert!(pool.contains(&tx_id));

        // Remove the transaction and check that it is no longer in the pool
        let removed = pool.remove_transaction(&tx_id);
        assert!(removed.is_some());
        assert!(!pool.contains(&tx_id));
    }

    #[test]
    fn test_enforce_basefee_with_handler_zero_allocation() {
        let mut f = MockTransactionFactory::default();
        let mut pool = ParkedPool::<BasefeeOrd<_>>::default();

        // Add multiple transactions across different fee ranges
        let sender_a = address!("0x000000000000000000000000000000000000000a");
        let sender_b = address!("0x000000000000000000000000000000000000000b");

        // Add transactions where nonce ordering allows proper processing:
        // Sender A: both transactions can afford basefee (500 >= 400, 600 >= 400)
        // Sender B: transaction cannot afford basefee (300 < 400)
        let txs = vec![
            f.validated_arc(
                MockTransaction::eip1559()
                    .set_sender(sender_a)
                    .set_nonce(0)
                    .set_max_fee(500)
                    .clone(),
            ),
            f.validated_arc(
                MockTransaction::eip1559()
                    .set_sender(sender_a)
                    .set_nonce(1)
                    .set_max_fee(600)
                    .clone(),
            ),
            f.validated_arc(
                MockTransaction::eip1559()
                    .set_sender(sender_b)
                    .set_nonce(0)
                    .set_max_fee(300)
                    .clone(),
            ),
        ];

        let expected_affordable = vec![txs[0].clone(), txs[1].clone()]; // Both sender A txs
        for tx in txs {
            pool.add_transaction(tx);
        }

        // Test the handler approach with zero allocations
        let mut processed_txs = Vec::new();
        let mut handler_call_count = 0;

        pool.enforce_basefee_with(400, |tx| {
            processed_txs.push(tx);
            handler_call_count += 1;
        });

        // Verify correct number of transactions processed
        assert_eq!(handler_call_count, 2);
        assert_eq!(processed_txs.len(), 2);

        // Verify the correct transactions were processed (those with fee >= 400)
        let processed_ids: Vec<_> = processed_txs.iter().map(|tx| *tx.id()).collect();
        for expected_tx in expected_affordable {
            assert!(processed_ids.contains(expected_tx.id()));
        }

        // Verify transactions were removed from pool
        assert_eq!(pool.len(), 1); // Only the 300 fee tx should remain
    }
}
