use crate::{executive_observer::ExecutiveObserver, substate::Substate};
use cfx_bytes::Bytes;
use cfx_types::{AddressWithSpace, U256};
use cfx_vm_types::Spec;
use primitives::{
    receipt::{SortedStorageChanges, StorageChange},
    LogEntry, TransactionWithSignature,
};
use typemap::ShareDebugMap;
use super::{
    fresh_executive::CostInfo,
    pre_checked_executive::{ExecutiveReturn, RefundInfo},
};
#[derive(Debug)]
pub struct Executed {
    pub base_gas: u64,
    pub gas_used: U256,
    pub fee: U256,
    pub burnt_fee: Option<U256>,
    pub gas_charged: U256,
    pub gas_sponsor_paid: bool,
    pub logs: Vec<LogEntry>,
    pub storage_sponsor_paid: bool,
    pub storage_collateralized: Vec<StorageChange>,
    pub storage_released: Vec<StorageChange>,
    pub contracts_created: Vec<AddressWithSpace>,
    pub output: Bytes,
    pub ext_result: ExecutedExt,
}
pub type ExecutedExt = ShareDebugMap;
impl Executed {
    pub(super) fn not_enough_balance_fee_charged(
        tx: &TransactionWithSignature, actual_gas_cost: &U256, cost: CostInfo,
        ext_result: ExecutedExt, spec: &Spec,
    ) -> Self {
        let gas_charged = if cost.gas_price == U256::zero() {
            U256::zero()
        } else {
            actual_gas_cost / cost.gas_price
        };
        let mut gas_sponsor_paid = cost.gas_sponsored;
        let mut storage_sponsor_paid = cost.storage_sponsored;
        if !spec.cip78b {
            gas_sponsor_paid = false;
            storage_sponsor_paid = false;
        }
        if spec.cip145_fix {
            gas_sponsor_paid = false;
        }
        let burnt_fee = spec.cip1559.then(|| {
            let target_burnt = tx.gas().saturating_mul(cost.burnt_gas_price);
            U256::min(*actual_gas_cost, target_burnt)
        });
        Self {
            gas_used: *tx.gas(),
            gas_charged,
            fee: *actual_gas_cost,
            burnt_fee,
            gas_sponsor_paid,
            logs: vec![],
            contracts_created: vec![],
            storage_sponsor_paid,
            storage_collateralized: Vec::new(),
            storage_released: Vec::new(),
            output: Default::default(),
            base_gas: cost.base_gas,
            ext_result,
        }
    }
    pub(super) fn execution_error_fully_charged(
        tx: &TransactionWithSignature, cost: CostInfo, ext_result: ExecutedExt,
        spec: &Spec,
    ) -> Self {
        let mut storage_sponsor_paid = cost.storage_sponsored;
        let mut gas_sponsor_paid = cost.gas_sponsored;
        if !spec.cip78b {
            gas_sponsor_paid = false;
            storage_sponsor_paid = false;
        }
        if spec.cip145 && !spec.cip145_fix {
            gas_sponsor_paid = false;
        }
        let fee = tx.gas().saturating_mul(cost.gas_price);
        let burnt_fee = spec
            .cip1559
            .then(|| tx.gas().saturating_mul(cost.burnt_gas_price));
        Self {
            gas_used: *tx.gas(),
            gas_charged: *tx.gas(),
            fee,
            burnt_fee,
            gas_sponsor_paid,
            logs: vec![],
            contracts_created: vec![],
            storage_sponsor_paid,
            storage_collateralized: Vec::new(),
            storage_released: Vec::new(),
            output: Default::default(),
            base_gas: cost.base_gas,
            ext_result,
        }
    }
    pub(super) fn from_executive_return(
        r: &ExecutiveReturn, refund_info: RefundInfo, cost: CostInfo,
        substate: Substate, ext_result: ExecutedExt, spec: &Spec,
    ) -> Self {
        let output = r.return_data.to_vec();
        let SortedStorageChanges {
            storage_collateralized,
            storage_released,
        } = if r.apply_state {
            substate.compute_storage_changes()
        } else {
            Default::default()
        };
        let RefundInfo {
            gas_used,
            gas_charged,
            fees_value: fee,
            burnt_fees_value: burnt_fee,
            ..
        } = refund_info;
        let mut storage_sponsor_paid = if spec.cip78a {
            cost.storage_sponsored
        } else {
            cost.storage_sponsor_eligible
        };
        let mut gas_sponsor_paid = cost.gas_sponsored;
        if !r.apply_state && !spec.cip78b {
            gas_sponsor_paid = false;
            storage_sponsor_paid = false;
        }
        Executed {
            gas_used,
            gas_charged,
            fee,
            burnt_fee,
            gas_sponsor_paid,
            logs: substate.logs.to_vec(),
            contracts_created: substate.contracts_created().to_vec(),
            storage_sponsor_paid,
            storage_collateralized,
            storage_released,
            output,
            base_gas: cost.base_gas,
            ext_result,
        }
    }
}
pub fn make_ext_result<O: ExecutiveObserver>(observer: O) -> ShareDebugMap {
    let mut ext_result = ShareDebugMap::custom();
    observer.drain_trace(&mut ext_result);
    ext_result
}