#[derive(Clone, Copy, Debug, DeriveMallocSizeOf, PartialEq)]
#[repr(u8)]
pub enum SnapshotKeptToProvideSyncStatus {
    No = 0,
    InfoOnly = 1,
    InfoAndSnapshot = 2,
}
impl Default for SnapshotKeptToProvideSyncStatus {
    fn default() -> Self { SnapshotKeptToProvideSyncStatus::No }
}
impl Encodable for SnapshotKeptToProvideSyncStatus {
    fn rlp_append(&self, s: &mut RlpStream) {
        s.append_internal(&(*self as u8));
    }
}
impl Decodable for SnapshotKeptToProvideSyncStatus {
    fn decode(rlp: &Rlp) -> std::result::Result<Self, DecoderError> {
        Ok(unsafe { std::mem::transmute(rlp.as_val::<u8>()?) })
    }
}
#[derive(
    Clone, Default, DeriveMallocSizeOf, RlpEncodable, RlpDecodable, Debug,
)]
pub struct SnapshotInfo {
    pub snapshot_info_kept_to_provide_sync: SnapshotKeptToProvideSyncStatus,
    pub serve_one_step_sync: bool,
    pub merkle_root: MerkleHash,
    pub parent_snapshot_height: u64,
    pub height: u64,
    pub parent_snapshot_epoch_id: EpochId,
    #[debug(skip)]
    pub pivot_chain_parts: Vec<EpochId>,
}
impl SnapshotInfo {
    pub fn genesis_snapshot_info() -> Self {
        Self {
            snapshot_info_kept_to_provide_sync: Default::default(),
            serve_one_step_sync: false,
            merkle_root: MERKLE_NULL_NODE,
            parent_snapshot_height: 0,
            height: 0,
            parent_snapshot_epoch_id: NULL_EPOCH,
            pivot_chain_parts: vec![NULL_EPOCH],
        }
    }
    pub fn get_snapshot_epoch_id(&self) -> &EpochId {
        self.pivot_chain_parts.last().unwrap()
    }
    pub fn get_epoch_id_at_height(&self, height: u64) -> Option<&EpochId> {
        if height < self.parent_snapshot_height {
            None
        } else if height == self.parent_snapshot_height {
            Some(&self.parent_snapshot_epoch_id)
        } else if height > self.height {
            None
        } else {
            unsafe {
                Some(self.pivot_chain_parts.get_unchecked(
                    (height - self.parent_snapshot_height - 1) as usize,
                ))
            }
        }
    }
}
pub trait OpenSnapshotMptTrait<'db> {
    type SnapshotDbBorrowSharedType: 'db + SnapshotMptTraitRead;
    type SnapshotDbBorrowMutType: 'db + SnapshotMptTraitRw;
    type SnapshotDbAsOwnedType: 'db + SnapshotMptTraitRw;
    fn open_snapshot_mpt_owned(
        &'db mut self,
    ) -> StorageResult<Self::SnapshotDbBorrowMutType>;
    fn open_snapshot_mpt_as_owned(
        &'db self,
    ) -> StorageResult<Self::SnapshotDbAsOwnedType>;
    fn open_snapshot_mpt_shared(
        &'db self,
    ) -> StorageResult<Self::SnapshotDbBorrowSharedType>;
}
pub trait SnapshotDbTrait:
    KeyValueDbTraitOwnedRead
    + KeyValueDbTraitRead
    + KeyValueDbTraitSingleWriter
    + for<'db> OpenSnapshotMptTrait<'db>
    + Sized
{
    type SnapshotKvdbIterTraitTag;
    type SnapshotKvdbIterType: WrappedTrait<
        dyn KeyValueDbIterableTrait<
            MptKeyValue,
            [u8],
            Self::SnapshotKvdbIterTraitTag,
        >,
    >;
    fn get_null_snapshot() -> Self;
    fn open(
        snapshot_path: &Path, readonly: bool,
        already_open_snapshots: &AlreadyOpenSnapshots<Self>,
        open_semaphore: &Arc<Semaphore>,
    ) -> StorageResult<Self>;
    fn create(
        snapshot_path: &Path,
        already_open_snapshots: &AlreadyOpenSnapshots<Self>,
        open_semaphore: &Arc<Semaphore>, mpt_table_in_current_db: bool,
    ) -> StorageResult<Self>;
    fn direct_merge(
        &mut self, old_snapshot_db: Option<&Arc<Self>>,
        mpt_snapshot: &mut Option<SnapshotMptDbSqlite>,
        recover_mpt_with_kv_snapshot_exist: bool,
        in_reconstruct_snapshot_state: bool,
    ) -> StorageResult<MerkleHash>;
    fn copy_and_merge(
        &mut self, old_snapshot_db: &Arc<Self>,
        mpt_snapshot_db: &mut Option<SnapshotMptDbSqlite>,
        in_reconstruct_snapshot_state: bool,
    ) -> StorageResult<MerkleHash>;
    fn start_transaction(&mut self) -> StorageResult<()>;
    fn commit_transaction(&mut self) -> StorageResult<()>;
    fn is_mpt_table_in_current_db(&self) -> bool;
    fn snapshot_kv_iterator(
        &self,
    ) -> StorageResult<
        Wrap<
            Self::SnapshotKvdbIterType,
            dyn KeyValueDbIterableTrait<
                MptKeyValue,
                [u8],
                Self::SnapshotKvdbIterTraitTag,
            >,
        >,
    >;
}
use crate::{
    impls::{
        errors::Result as StorageResult,
        storage_db::{
            snapshot_db_manager_sqlite::AlreadyOpenSnapshots,
            snapshot_mpt_db_sqlite::SnapshotMptDbSqlite,
        },
    },
    storage_db::{
        KeyValueDbIterableTrait, KeyValueDbTraitOwnedRead, KeyValueDbTraitRead,
        KeyValueDbTraitSingleWriter, SnapshotMptTraitRead, SnapshotMptTraitRw,
    },
    utils::wrap::{Wrap, WrappedTrait},
    MptKeyValue,
};
use derive_more::Debug;
use malloc_size_of_derive::MallocSizeOf as DeriveMallocSizeOf;
use primitives::{EpochId, MerkleHash, MERKLE_NULL_NODE, NULL_EPOCH};
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
use rlp_derive::{RlpDecodable, RlpEncodable};
use std::{path::Path, sync::Arc};
use tokio::sync::Semaphore;