use crate::{
    bytes::Bytes, transaction::AccessListItem, Action, SignedTransaction,
    Transaction, TransactionWithSignature,
    TransactionWithSignatureSerializePart,
};
use cfx_types::{AddressWithSpace, H256, U256};
use cfxkey::Address;
use rlp_derive::{RlpDecodable, RlpEncodable};
use serde_derive::{Deserialize, Serialize};
use super::{AccessList, AuthorizationListItem};
#[derive(
    Default,
    Debug,
    Clone,
    Eq,
    PartialEq,
    RlpEncodable,
    RlpDecodable,
    Serialize,
    Deserialize,
)]
pub struct NativeTransaction {
    pub nonce: U256,
    pub gas_price: U256,
    pub gas: U256,
    pub action: Action,
    pub value: U256,
    pub storage_limit: u64,
    pub epoch_height: u64,
    pub chain_id: u32,
    pub data: Bytes,
}
impl NativeTransaction {
    pub fn fake_sign(self, from: AddressWithSpace) -> SignedTransaction {
        SignedTransaction {
            transaction: TransactionWithSignature {
                transaction: TransactionWithSignatureSerializePart {
                    unsigned: Transaction::Native(
                        TypedNativeTransaction::Cip155(self),
                    ),
                    r: U256::one(),
                    s: U256::one(),
                    v: 0,
                },
                hash: H256::zero(),
                rlp_size: None,
            }
            .compute_hash(),
            sender: from.address,
            public: None,
        }
    }
}
#[derive(
    Default,
    Debug,
    Clone,
    Eq,
    PartialEq,
    RlpEncodable,
    RlpDecodable,
    Serialize,
    Deserialize,
)]
pub struct Cip2930Transaction {
    pub nonce: U256,
    pub gas_price: U256,
    pub gas: U256,
    pub action: Action,
    pub value: U256,
    pub storage_limit: u64,
    pub epoch_height: u64,
    pub chain_id: u32,
    pub data: Bytes,
    pub access_list: Vec<AccessListItem>,
}
#[derive(
    Default,
    Debug,
    Clone,
    Eq,
    PartialEq,
    RlpEncodable,
    RlpDecodable,
    Serialize,
    Deserialize,
)]
pub struct Cip1559Transaction {
    pub nonce: U256,
    pub max_priority_fee_per_gas: U256,
    pub max_fee_per_gas: U256,
    pub gas: U256,
    pub action: Action,
    pub value: U256,
    pub storage_limit: u64,
    pub epoch_height: u64,
    pub chain_id: u32,
    pub data: Bytes,
    pub access_list: Vec<AccessListItem>,
}
#[derive(
    Default,
    Debug,
    Clone,
    Eq,
    PartialEq,
    RlpEncodable,
    RlpDecodable,
    Serialize,
    Deserialize,
)]
pub struct Cip7702Transaction {
    pub nonce: U256,
    pub max_priority_fee_per_gas: U256,
    pub max_fee_per_gas: U256,
    pub gas: U256,
    pub destination: Address,
    pub value: U256,
    pub storage_limit: u64,
    pub epoch_height: u64,
    pub chain_id: u32,
    pub data: Bytes,
    pub access_list: Vec<AccessListItem>,
    pub authorization_list: Vec<AuthorizationListItem>,
}
macro_rules! access_common_ref {
    ($field:ident, $ty:ty) => {
        pub fn $field(&self) -> &$ty {
            match self {
                TypedNativeTransaction::Cip155(tx) => &tx.$field,
                TypedNativeTransaction::Cip2930(tx) => &tx.$field,
                TypedNativeTransaction::Cip1559(tx) => &tx.$field,
            }
        }
    };
}
impl TypedNativeTransaction {
    access_common_ref!(gas, U256);
    access_common_ref!(data, Bytes);
    access_common_ref!(nonce, U256);
    access_common_ref!(value, U256);
    access_common_ref!(chain_id, u32);
    access_common_ref!(epoch_height, u64);
    access_common_ref!(storage_limit, u64);
    pub fn action(&self) -> Action {
        match self {
            TypedNativeTransaction::Cip155(tx) => tx.action,
            TypedNativeTransaction::Cip2930(tx) => tx.action,
            TypedNativeTransaction::Cip1559(tx) => tx.action,
        }
    }
    pub fn gas_price(&self) -> &U256 {
        match self {
            Cip155(tx) => &tx.gas_price,
            Cip2930(tx) => &tx.gas_price,
            Cip1559(tx) => &tx.max_fee_per_gas,
        }
    }
    pub fn max_priority_gas_price(&self) -> &U256 {
        match self {
            Cip155(tx) => &tx.gas_price,
            Cip2930(tx) => &tx.gas_price,
            Cip1559(tx) => &tx.max_priority_fee_per_gas,
        }
    }
    pub fn nonce_mut(&mut self) -> &mut U256 {
        match self {
            Cip155(tx) => &mut tx.nonce,
            Cip2930(tx) => &mut tx.nonce,
            Cip1559(tx) => &mut tx.nonce,
        }
    }
    pub fn data_mut(&mut self) -> &mut Vec<u8> {
        match self {
            Cip155(tx) => &mut tx.data,
            Cip2930(tx) => &mut tx.data,
            Cip1559(tx) => &mut tx.data,
        }
    }
    pub fn access_list(&self) -> Option<&AccessList> {
        match self {
            Cip155(_tx) => None,
            Cip2930(tx) => Some(&tx.access_list),
            Cip1559(tx) => Some(&tx.access_list),
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TypedNativeTransaction {
    Cip155(NativeTransaction),
    Cip2930(Cip2930Transaction),
    Cip1559(Cip1559Transaction),
}
impl TypedNativeTransaction {
    pub fn fake_sign_rpc(self, from: AddressWithSpace) -> SignedTransaction {
        SignedTransaction {
            transaction: TransactionWithSignature {
                transaction: TransactionWithSignatureSerializePart {
                    unsigned: Transaction::Native(self),
                    r: U256::one(),
                    s: U256::one(),
                    v: 0,
                },
                hash: H256::zero(),
                rlp_size: None,
            }
            .compute_hash(),
            sender: from.address,
            public: None,
        }
    }
}
use TypedNativeTransaction::*;