use crate::inspector::Cheatcodes;
use alloy_primitives::{Address, Bytes, U256};
use foundry_evm_core::evm::{FoundryContextFor, FoundryEvmNetwork};
use revm::{
    context::{ContextTr, JournalTr},
    interpreter::{CreateInputs, CreateScheme},
};

/// Common behaviour of legacy and EOF create inputs.
pub(crate) trait CommonCreateInput {
    fn caller(&self) -> Address;
    fn gas_limit(&self) -> u64;
    fn value(&self) -> U256;
    fn init_code(&self) -> Bytes;
    fn scheme(&self) -> Option<CreateScheme>;
    fn set_caller(&mut self, caller: Address);
    fn log_debug<FEN: FoundryEvmNetwork>(
        &self,
        cheatcode: &mut Cheatcodes<FEN>,
        scheme: &CreateScheme,
    );
    fn allow_cheatcodes<FEN: FoundryEvmNetwork>(
        &self,
        cheatcodes: &mut Cheatcodes<FEN>,
        ecx: &mut FoundryContextFor<'_, FEN>,
    ) -> Address;
}

impl CommonCreateInput for &mut CreateInputs {
    fn caller(&self) -> Address {
        CreateInputs::caller(self)
    }
    fn gas_limit(&self) -> u64 {
        CreateInputs::gas_limit(self)
    }
    fn value(&self) -> U256 {
        CreateInputs::value(self)
    }
    fn init_code(&self) -> Bytes {
        CreateInputs::init_code(self).clone()
    }
    fn scheme(&self) -> Option<CreateScheme> {
        Some(CreateInputs::scheme(self))
    }
    fn set_caller(&mut self, caller: Address) {
        CreateInputs::set_call(self, caller);
    }
    fn log_debug<FEN: FoundryEvmNetwork>(
        &self,
        cheatcode: &mut Cheatcodes<FEN>,
        scheme: &CreateScheme,
    ) {
        let kind = match scheme {
            CreateScheme::Create => "create",
            CreateScheme::Create2 { .. } => "create2",
            CreateScheme::Custom { .. } => "custom",
        };
        debug!(target: "cheatcodes", tx=?cheatcode.broadcastable_transactions.back().unwrap(), "broadcastable {kind}");
    }
    fn allow_cheatcodes<FEN: FoundryEvmNetwork>(
        &self,
        cheatcodes: &mut Cheatcodes<FEN>,
        ecx: &mut FoundryContextFor<'_, FEN>,
    ) -> Address {
        let caller = CreateInputs::caller(self);
        let old_nonce =
            ecx.journal().evm_state().get(&caller).map(|acc| acc.info.nonce).unwrap_or_default();
        let created_address = self.created_address(old_nonce);
        cheatcodes.allow_cheatcodes_on_create(ecx, caller, created_address);
        created_address
    }
}
