use indexmap::IndexMap;
use solana_signature::Signature;

use crate::types::TransactionResult;

#[derive(Clone, Debug)]
pub(crate) struct TransactionHistory {
    entries: IndexMap<Signature, TransactionResult>,
    max_entries: usize,
}

impl TransactionHistory {
    pub(crate) fn new() -> Self {
        Self { entries: IndexMap::with_capacity(32), max_entries: 32 }
    }

    pub(crate) fn set_capacity(&mut self, new_cap: usize) {
        self.max_entries = new_cap;
        while self.entries.len() > self.max_entries {
            self.entries.shift_remove_index(0);
        }
    }

    pub(crate) fn get_transaction(&self, signature: &Signature) -> Option<&TransactionResult> {
        self.entries.get(signature)
    }

    pub(crate) fn add_new_transaction(&mut self, signature: Signature, result: TransactionResult) {
        if self.max_entries != 0 {
            if self.entries.len() == self.max_entries {
                self.entries.shift_remove_index(0);
            }
            self.entries.insert(signature, result);
        }
    }

    pub(crate) fn check_transaction(&self, signature: &Signature) -> bool {
        self.entries.contains_key(signature)
    }
}

#[cfg(test)]
mod tests {
    use solana_transaction_error::TransactionError;

    use super::*;
    use crate::types::{FailedTransactionMetadata, TransactionMetadata};

    #[test]
    fn set_capacity_limits_history_by_entry_count() {
        let mut history = TransactionHistory::new();
        history.set_capacity(1);

        let first = Signature::from([1; 64]);
        let second = Signature::from([2; 64]);
        let result = Ok(TransactionMetadata::default());

        history.add_new_transaction(first, result.clone());
        history.add_new_transaction(second, result);

        assert!(!history.check_transaction(&first));
        assert!(history.check_transaction(&second));
    }

    #[test]
    fn zero_capacity_disables_history_storage() {
        let mut history = TransactionHistory::new();
        history.set_capacity(0);

        let signature = Signature::from([3; 64]);
        history.add_new_transaction(
            signature,
            Err(FailedTransactionMetadata {
                err: TransactionError::AlreadyProcessed,
                meta: TransactionMetadata::default(),
            }),
        );

        assert!(!history.check_transaction(&signature));
        assert!(history.get_transaction(&signature).is_none());
    }
}
