//! Auto-restore from Parquet snapshot if a partition is empty.
//!
//! Checks whether a backend is empty and, if so, attempts to load an existing
//! Parquet snapshot from the given storage path. If no snapshot is found,
//! logs an informational message and returns `Ok(false)` so startup can
//! continue without failing.
//!
//! When a `WalConfig` is supplied and WAL is enabled, the WAL is replayed on
//! top of the restored snapshot to recover mutations that occurred after the
//! last Parquet checkpoint.

#[cfg(all(feature = "distributed", feature = "self-heal"))]
use std::path::Path;
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use std::sync::Arc;
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use tokio::sync::RwLock;

#[cfg(all(feature = "distributed", feature = "self-heal"))]
use crate::distributed::graph::PartitionedGraphBackend;
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use crate::distributed::storage::parquet_serde::{load_partition_metadata, load_partition_from_parquet};
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use crate::distributed::storage::wal::{WalConfig, WriteAheadLog};
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use crate::error::CypherResult;
#[cfg(all(feature = "distributed", feature = "self-heal"))]
use crate::graph::GraphBackend;

/// Restore a backend from a Parquet snapshot if it is currently empty.
///
/// # Parameters
/// - `backend`      — The partition backend (wrapped in `Arc<RwLock<_>>`).
/// - `storage_path` — Local filesystem path to the partition snapshot directory.
/// - `wal_config`   — Optional WAL configuration. When `Some` and `enabled`,
///                    the WAL is replayed after the snapshot restore.
///
/// # Returns
/// - `Ok(true)`  — A snapshot was found and the restore was performed.
/// - `Ok(false)` — The backend was non-empty, or no snapshot exists yet.
/// - `Err(_)`    — An unexpected I/O or deserialization error occurred.
#[cfg(all(feature = "distributed", feature = "self-heal"))]
pub async fn restore_if_empty(
    backend: Arc<RwLock<PartitionedGraphBackend>>,
    storage_path: &str,
    wal_config: Option<&WalConfig>,
) -> CypherResult<bool> {
    // Check whether the backend already contains data.
    {
        let guard = backend.read().await;
        let node_count = guard.all_nodes().count();
        if node_count > 0 {
            tracing::debug!(
                node_count,
                storage_path,
                "partition is non-empty; skipping snapshot restore"
            );
            return Ok(false);
        }
    }

    let partition_dir = Path::new(storage_path);

    // Check whether a snapshot exists (look for metadata.json as the sentinel).
    let metadata_path = partition_dir.join("metadata.json");
    if !metadata_path.exists() {
        tracing::info!(
            storage_path,
            "no snapshot found; starting with empty partition"
        );
        return Ok(false);
    }

    // Load metadata to confirm the snapshot is readable.
    let meta = match load_partition_metadata(partition_dir) {
        Ok(m) => m,
        Err(e) => {
            tracing::warn!(
                error = %e,
                storage_path,
                "failed to read snapshot metadata; starting with empty partition"
            );
            return Ok(false);
        }
    };

    tracing::info!(
        partition_id = meta.partition_id,
        node_count   = meta.node_count,
        edge_count   = meta.edge_count,
        storage_path,
        "snapshot found — restoring partition from Parquet"
    );

    // Perform the full restore.
    let mut guard = backend.write().await;
    match load_partition_from_parquet(&mut *guard, partition_dir) {
        Ok((nodes, edges)) => {
            tracing::info!(
                nodes,
                edges,
                storage_path,
                "partition successfully restored from Parquet snapshot"
            );

            // Replay WAL on top of the snapshot if configured.
            if let Some(cfg) = wal_config {
                if cfg.enabled {
                    match WriteAheadLog::open(cfg, storage_path) {
                        Ok(wal) => {
                            match wal.replay(&mut *guard) {
                                Ok(replayed) => {
                                    tracing::info!(
                                        replayed,
                                        storage_path,
                                        "WAL replay complete"
                                    );
                                }
                                Err(e) => {
                                    tracing::error!(
                                        error = %e,
                                        storage_path,
                                        "WAL replay failed; partition state may be incomplete"
                                    );
                                }
                            }
                        }
                        Err(e) => {
                            tracing::error!(
                                error = %e,
                                storage_path,
                                "failed to open WAL for replay; proceeding without WAL replay"
                            );
                        }
                    }
                }
            }

            Ok(true)
        }
        Err(e) => {
            tracing::error!(
                error = %e,
                storage_path,
                "Parquet restore failed; partition will start empty"
            );
            // Non-fatal: let the pod start rather than crash-loop.
            Ok(false)
        }
    }
}
