//! Storage service.

use std::path::PathBuf;
use std::sync::Arc;

use tokio::sync::RwLock;
use tracing::{debug, info, warn};

use synor_storage::{
    cf, BlockBody, BlockStore, ChainState, Database, DatabaseConfig, GhostdagStore, HeaderStore,
    MetadataStore, RelationsStore, StoredGhostdagData, StoredRelations, StoredUtxo,
    TransactionStore, UtxoStore,
};
use synor_types::{BlockHeader, BlockId, Hash256, Transaction, TransactionId};

use crate::config::NodeConfig;

/// Block data (convenience struct for RPC layer and import/export).
#[derive(Clone, Debug, borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct BlockData {
    pub hash: [u8; 32],
    pub header: Vec<u8>,
    pub body: Vec<u8>,
}

/// Storage service manages persistent data using RocksDB.
pub struct StorageService {
    /// Data directory.
    data_dir: PathBuf,

    /// Database path.
    db_path: PathBuf,

    /// Database configuration.
    db_config: DatabaseConfig,

    /// The underlying RocksDB database (initialized on start).
    database: RwLock<Option<Arc<Database>>>,

    /// Header store.
    header_store: RwLock<Option<HeaderStore>>,

    /// Block store.
    block_store: RwLock<Option<BlockStore>>,

    /// Transaction store.
    tx_store: RwLock<Option<TransactionStore>>,

    /// UTXO store.
    utxo_store: RwLock<Option<UtxoStore>>,

    /// Relations store.
    relations_store: RwLock<Option<RelationsStore>>,

    /// GHOSTDAG store.
    ghostdag_store: RwLock<Option<GhostdagStore>>,

    /// Metadata store.
    metadata_store: RwLock<Option<MetadataStore>>,

    /// Is running.
    running: RwLock<bool>,
}

impl StorageService {
    /// Creates a new storage service.
    pub async fn new(config: &NodeConfig) -> anyhow::Result<Self> {
        let db_path = config.data_dir.join("db");

        // Ensure data directory exists
        std::fs::create_dir_all(&db_path)?;

        // Configure database based on node config
        let db_config = DatabaseConfig {
            max_open_files: config.storage.max_open_files,
            write_buffer_size: 64 * 1024 * 1024, // 64 MB
            max_write_buffer_number: 3,
            target_file_size_base: 64 * 1024 * 1024,
            max_total_wal_size: 256 * 1024 * 1024,
            enable_compression: config.storage.compression,
            block_cache_size: config.storage.cache_size_mb * 1024 * 1024,
            enable_statistics: false,
            create_if_missing: true,
            parallelism: num_cpus::get() as i32,
        };

        Ok(StorageService {
            data_dir: config.data_dir.clone(),
            db_path,
            db_config,
            database: RwLock::new(None),
            header_store: RwLock::new(None),
            block_store: RwLock::new(None),
            tx_store: RwLock::new(None),
            utxo_store: RwLock::new(None),
            relations_store: RwLock::new(None),
            ghostdag_store: RwLock::new(None),
            metadata_store: RwLock::new(None),
            running: RwLock::new(false),
        })
    }

    /// Starts the storage service by opening RocksDB.
    pub async fn start(&self) -> anyhow::Result<()> {
        info!(path = %self.db_path.display(), "Starting storage service");

        // Open the database
        let db = Database::open(&self.db_path, &self.db_config)
            .map_err(|e| anyhow::anyhow!("Failed to open database: {}", e))?;
        let db = Arc::new(db);

        info!("Database opened successfully");

        // Initialize all stores
        *self.header_store.write().await = Some(HeaderStore::new(Arc::clone(&db)));
        *self.block_store.write().await = Some(BlockStore::new(Arc::clone(&db)));
        *self.tx_store.write().await = Some(TransactionStore::new(Arc::clone(&db)));
        *self.utxo_store.write().await = Some(UtxoStore::new(Arc::clone(&db)));
        *self.relations_store.write().await = Some(RelationsStore::new(Arc::clone(&db)));
        *self.ghostdag_store.write().await = Some(GhostdagStore::new(Arc::clone(&db)));
        *self.metadata_store.write().await = Some(MetadataStore::new(Arc::clone(&db)));

        *self.database.write().await = Some(db);
        *self.running.write().await = true;

        info!("Storage service started with all stores initialized");
        Ok(())
    }

    /// Stops the storage service.
    pub async fn stop(&self) -> anyhow::Result<()> {
        info!("Stopping storage service");

        // Clear all stores first
        *self.header_store.write().await = None;
        *self.block_store.write().await = None;
        *self.tx_store.write().await = None;
        *self.utxo_store.write().await = None;
        *self.relations_store.write().await = None;
        *self.ghostdag_store.write().await = None;
        *self.metadata_store.write().await = None;

        // Flush and close database
        if let Some(db) = self.database.write().await.take() {
            if let Err(e) = db.flush() {
                warn!("Error flushing database: {}", e);
            }
        }

        *self.running.write().await = false;
        info!("Storage service stopped");
        Ok(())
    }

    /// Returns true if the service is running.
    pub async fn is_running(&self) -> bool {
        *self.running.read().await
    }

    /// Gets the underlying database (for advanced operations).
    pub async fn database(&self) -> Option<Arc<Database>> {
        self.database.read().await.clone()
    }

    // ==================== Header Operations ====================

    /// Stores a block header.
    pub async fn put_header(&self, header: &BlockHeader) -> anyhow::Result<()> {
        let store = self.header_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(header)
            .map_err(|e| anyhow::anyhow!("Failed to store header: {}", e))
    }

    /// Gets a block header by hash.
    pub async fn get_header(&self, hash: &Hash256) -> anyhow::Result<Option<BlockHeader>> {
        let store = self.header_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(hash)
            .map_err(|e| anyhow::anyhow!("Failed to get header: {}", e))
    }

    /// Checks if a header exists.
    pub async fn has_header(&self, hash: &Hash256) -> bool {
        let store = self.header_store.read().await;
        if let Some(store) = store.as_ref() {
            store.exists(hash).unwrap_or(false)
        } else {
            false
        }
    }

    /// Gets header by height.
    pub async fn get_header_by_height(&self, height: u64) -> anyhow::Result<Option<BlockHeader>> {
        let store = self.header_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_by_height(height)
            .map_err(|e| anyhow::anyhow!("Failed to get header by height: {}", e))
    }

    /// Indexes a header by height.
    pub async fn index_header_by_height(&self, height: u64, hash: &Hash256) -> anyhow::Result<()> {
        let store = self.header_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .index_by_height(height, hash)
            .map_err(|e| anyhow::anyhow!("Failed to index header: {}", e))
    }

    // ==================== Block Body Operations ====================

    /// Stores a block body.
    pub async fn put_block_body(&self, hash: &Hash256, body: &BlockBody) -> anyhow::Result<()> {
        let store = self.block_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(hash, body)
            .map_err(|e| anyhow::anyhow!("Failed to store block body: {}", e))
    }

    /// Gets a block body by hash.
    pub async fn get_block_body(&self, hash: &Hash256) -> anyhow::Result<Option<BlockBody>> {
        let store = self.block_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(hash)
            .map_err(|e| anyhow::anyhow!("Failed to get block body: {}", e))
    }

    /// Checks if block exists.
    pub async fn has_block(&self, hash: &Hash256) -> bool {
        let store = self.block_store.read().await;
        if let Some(store) = store.as_ref() {
            store.exists(hash).unwrap_or(false)
        } else {
            false
        }
    }

    /// Legacy method: Stores a block (header + body as raw bytes).
    pub async fn put_block(&self, block: &BlockData) -> anyhow::Result<()> {
        debug!(hash = hex::encode(&block.hash[..8]), "Storing block");
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;

        // Store header bytes
        db.put(cf::HEADERS, &block.hash, &block.header)
            .map_err(|e| anyhow::anyhow!("Failed to store header: {}", e))?;

        // Store body bytes
        db.put(cf::BLOCKS, &block.hash, &block.body)
            .map_err(|e| anyhow::anyhow!("Failed to store body: {}", e))?;

        Ok(())
    }

    /// Legacy method: Gets a block by hash (raw bytes).
    pub async fn get_block(&self, hash: &[u8; 32]) -> anyhow::Result<Option<BlockData>> {
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;

        let header = db
            .get(cf::HEADERS, hash)
            .map_err(|e| anyhow::anyhow!("Failed to get header: {}", e))?;
        let body = db
            .get(cf::BLOCKS, hash)
            .map_err(|e| anyhow::anyhow!("Failed to get body: {}", e))?;

        match (header, body) {
            (Some(h), Some(b)) => Ok(Some(BlockData {
                hash: *hash,
                header: h,
                body: b,
            })),
            _ => Ok(None),
        }
    }

    // ==================== Transaction Operations ====================

    /// Stores a transaction.
    pub async fn put_transaction(&self, tx: &Transaction) -> anyhow::Result<()> {
        let store = self.tx_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(tx)
            .map_err(|e| anyhow::anyhow!("Failed to store transaction: {}", e))
    }

    /// Gets a transaction by ID.
    pub async fn get_transaction(
        &self,
        txid: &TransactionId,
    ) -> anyhow::Result<Option<Transaction>> {
        let store = self.tx_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(txid)
            .map_err(|e| anyhow::anyhow!("Failed to get transaction: {}", e))
    }

    /// Checks if a transaction exists.
    pub async fn has_transaction(&self, txid: &TransactionId) -> bool {
        let store = self.tx_store.read().await;
        if let Some(store) = store.as_ref() {
            store.exists(txid).unwrap_or(false)
        } else {
            false
        }
    }

    // ==================== UTXO Operations ====================

    /// Gets a UTXO.
    pub async fn get_utxo(
        &self,
        txid: &TransactionId,
        index: u32,
    ) -> anyhow::Result<Option<StoredUtxo>> {
        let store = self.utxo_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(txid, index)
            .map_err(|e| anyhow::anyhow!("Failed to get UTXO: {}", e))
    }

    /// Stores a UTXO.
    pub async fn put_utxo(
        &self,
        txid: &TransactionId,
        index: u32,
        utxo: &StoredUtxo,
    ) -> anyhow::Result<()> {
        let store = self.utxo_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(txid, index, utxo)
            .map_err(|e| anyhow::anyhow!("Failed to store UTXO: {}", e))
    }

    /// Deletes a UTXO (marks as spent).
    pub async fn delete_utxo(&self, txid: &TransactionId, index: u32) -> anyhow::Result<()> {
        let store = self.utxo_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .delete(txid, index)
            .map_err(|e| anyhow::anyhow!("Failed to delete UTXO: {}", e))
    }

    /// Checks if a UTXO exists (is unspent).
    pub async fn has_utxo(&self, txid: &TransactionId, index: u32) -> bool {
        let store = self.utxo_store.read().await;
        if let Some(store) = store.as_ref() {
            store.exists(txid, index).unwrap_or(false)
        } else {
            false
        }
    }

    /// Gets all UTXOs for a transaction.
    pub async fn get_utxos_by_tx(
        &self,
        txid: &TransactionId,
    ) -> anyhow::Result<Vec<(u32, StoredUtxo)>> {
        let store = self.utxo_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_by_tx(txid)
            .map_err(|e| anyhow::anyhow!("Failed to get UTXOs: {}", e))
    }

    // ==================== DAG Relations Operations ====================

    /// Stores DAG relations for a block.
    pub async fn put_relations(
        &self,
        block_id: &BlockId,
        relations: &StoredRelations,
    ) -> anyhow::Result<()> {
        let store = self.relations_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(block_id, relations)
            .map_err(|e| anyhow::anyhow!("Failed to store relations: {}", e))
    }

    /// Gets DAG relations for a block.
    pub async fn get_relations(
        &self,
        block_id: &BlockId,
    ) -> anyhow::Result<Option<StoredRelations>> {
        let store = self.relations_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get relations: {}", e))
    }

    /// Gets parents of a block.
    pub async fn get_parents(&self, block_id: &BlockId) -> anyhow::Result<Vec<BlockId>> {
        let store = self.relations_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_parents(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get parents: {}", e))
    }

    /// Gets children of a block.
    pub async fn get_children(&self, block_id: &BlockId) -> anyhow::Result<Vec<BlockId>> {
        let store = self.relations_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_children(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get children: {}", e))
    }

    /// Adds a child to a block's relations.
    pub async fn add_child(&self, parent_id: &BlockId, child_id: BlockId) -> anyhow::Result<()> {
        let store = self.relations_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .add_child(parent_id, child_id)
            .map_err(|e| anyhow::anyhow!("Failed to add child: {}", e))
    }

    // ==================== GHOSTDAG Operations ====================

    /// Stores GHOSTDAG data for a block.
    pub async fn put_ghostdag(
        &self,
        block_id: &BlockId,
        data: &StoredGhostdagData,
    ) -> anyhow::Result<()> {
        let store = self.ghostdag_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .put(block_id, data)
            .map_err(|e| anyhow::anyhow!("Failed to store GHOSTDAG data: {}", e))
    }

    /// Gets GHOSTDAG data for a block.
    pub async fn get_ghostdag(
        &self,
        block_id: &BlockId,
    ) -> anyhow::Result<Option<StoredGhostdagData>> {
        let store = self.ghostdag_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get GHOSTDAG data: {}", e))
    }

    /// Gets the blue score of a block.
    pub async fn get_blue_score(&self, block_id: &BlockId) -> anyhow::Result<Option<u64>> {
        let store = self.ghostdag_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_blue_score(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get blue score: {}", e))
    }

    /// Gets the selected parent of a block.
    pub async fn get_selected_parent(&self, block_id: &BlockId) -> anyhow::Result<Option<BlockId>> {
        let store = self.ghostdag_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_selected_parent(block_id)
            .map_err(|e| anyhow::anyhow!("Failed to get selected parent: {}", e))
    }

    // ==================== Metadata Operations ====================

    /// Gets current DAG tips.
    pub async fn get_tips(&self) -> anyhow::Result<Vec<BlockId>> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_tips()
            .map_err(|e| anyhow::anyhow!("Failed to get tips: {}", e))
    }

    /// Sets current DAG tips.
    pub async fn set_tips(&self, tips: &[BlockId]) -> anyhow::Result<()> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .set_tips(tips)
            .map_err(|e| anyhow::anyhow!("Failed to set tips: {}", e))
    }

    /// Gets the current chain tip (first tip, for legacy compatibility).
    pub async fn get_tip(&self) -> Option<[u8; 32]> {
        if let Ok(tips) = self.get_tips().await {
            tips.first().map(|id| *id.as_bytes())
        } else {
            None
        }
    }

    /// Sets the chain tip (for legacy compatibility).
    pub async fn set_tip(&self, hash: &[u8; 32]) -> anyhow::Result<()> {
        let block_id = BlockId::from_bytes(*hash);
        self.set_tips(&[block_id]).await
    }

    /// Gets the genesis block ID.
    pub async fn get_genesis(&self) -> anyhow::Result<Option<BlockId>> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_genesis()
            .map_err(|e| anyhow::anyhow!("Failed to get genesis: {}", e))
    }

    /// Sets the genesis block ID.
    pub async fn set_genesis(&self, genesis: &BlockId) -> anyhow::Result<()> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .set_genesis(genesis)
            .map_err(|e| anyhow::anyhow!("Failed to set genesis: {}", e))
    }

    /// Gets the chain state.
    pub async fn get_chain_state(&self) -> anyhow::Result<Option<ChainState>> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_chain_state()
            .map_err(|e| anyhow::anyhow!("Failed to get chain state: {}", e))
    }

    /// Sets the chain state.
    pub async fn set_chain_state(&self, state: &ChainState) -> anyhow::Result<()> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .set_chain_state(state)
            .map_err(|e| anyhow::anyhow!("Failed to set chain state: {}", e))
    }

    /// Gets current height from chain state.
    pub async fn get_height(&self) -> u64 {
        if let Ok(Some(state)) = self.get_chain_state().await {
            state.max_blue_score
        } else {
            0
        }
    }

    /// Gets the pruning point.
    pub async fn get_pruning_point(&self) -> anyhow::Result<Option<BlockId>> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .get_pruning_point()
            .map_err(|e| anyhow::anyhow!("Failed to get pruning point: {}", e))
    }

    /// Sets the pruning point.
    pub async fn set_pruning_point(&self, point: &BlockId) -> anyhow::Result<()> {
        let store = self.metadata_store.read().await;
        let store = store
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        store
            .set_pruning_point(point)
            .map_err(|e| anyhow::anyhow!("Failed to set pruning point: {}", e))
    }

    // ==================== Contract Storage ====================

    /// Gets contract storage value.
    pub async fn get_contract_storage(
        &self,
        contract: &[u8; 32],
        key: &[u8; 32],
    ) -> anyhow::Result<Option<Vec<u8>>> {
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;

        // Create composite key: contract_address || storage_key
        let mut composite_key = Vec::with_capacity(64);
        composite_key.extend_from_slice(contract);
        composite_key.extend_from_slice(key);

        // Use metadata CF for contract storage (could add dedicated CF later)
        db.get(cf::METADATA, &composite_key)
            .map_err(|e| anyhow::anyhow!("Failed to get contract storage: {}", e))
    }

    /// Sets contract storage value.
    pub async fn put_contract_storage(
        &self,
        contract: &[u8; 32],
        key: &[u8; 32],
        value: &[u8],
    ) -> anyhow::Result<()> {
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;

        let mut composite_key = Vec::with_capacity(64);
        composite_key.extend_from_slice(contract);
        composite_key.extend_from_slice(key);

        db.put(cf::METADATA, &composite_key, value)
            .map_err(|e| anyhow::anyhow!("Failed to put contract storage: {}", e))
    }

    // ==================== Maintenance Operations ====================

    /// Compacts the database.
    pub async fn compact(&self) -> anyhow::Result<()> {
        info!("Compacting database");
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        db.compact()
            .map_err(|e| anyhow::anyhow!("Failed to compact database: {}", e))
    }

    /// Flushes pending writes to disk.
    pub async fn flush(&self) -> anyhow::Result<()> {
        let db = self.database.read().await;
        let db = db
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("Storage not initialized"))?;
        db.flush()
            .map_err(|e| anyhow::anyhow!("Failed to flush database: {}", e))
    }

    /// Gets database statistics.
    pub async fn stats(&self) -> StorageStats {
        let db = self.database.read().await;

        if let Some(db) = db.as_ref() {
            let headers_size = db.cf_size(cf::HEADERS).unwrap_or(0);
            let blocks_size = db.cf_size(cf::BLOCKS).unwrap_or(0);
            let utxos_size = db.cf_size(cf::UTXOS).unwrap_or(0);
            let total_size = headers_size
                + blocks_size
                + utxos_size
                + db.cf_size(cf::TRANSACTIONS).unwrap_or(0)
                + db.cf_size(cf::RELATIONS).unwrap_or(0)
                + db.cf_size(cf::GHOSTDAG).unwrap_or(0)
                + db.cf_size(cf::METADATA).unwrap_or(0);

            // Estimate counts from size (rough approximation)
            let blocks_count = blocks_size / 1000; // ~1KB per block body
            let utxo_count = utxos_size / 50; // ~50 bytes per UTXO

            StorageStats {
                blocks_count,
                utxo_count,
                disk_usage_bytes: total_size,
                cache_hits: 0, // Would need cache instrumentation
                cache_misses: 0,
            }
        } else {
            StorageStats::default()
        }
    }
}

/// Storage statistics.
#[derive(Clone, Debug, Default)]
pub struct StorageStats {
    pub blocks_count: u64,
    pub utxo_count: u64,
    pub disk_usage_bytes: u64,
    pub cache_hits: u64,
    pub cache_misses: u64,
}
