//! Crash-recovery manifest for the graph storage engine.
//!
//! Physical copy from geometric_db_concept/crates/geographdb/src/storage/manifest.rs,
//! adapted to use geographdb-core's crate path conventions.

use bytemuck::{Pod, Zeroable};

/// Magic number for manifest files: "GDMM" (GeoMetriDB Manifest)
pub const MANIFEST_MAGIC: u32 = 0x47444D4D;

/// Current manifest format version
pub const MANIFEST_VERSION: u32 = 1;

/// Manifest metadata for the database.
///
/// Contains critical metadata that allows the database to recover its state
/// after startup. It is written atomically to disk.
#[repr(C)]
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
pub struct Manifest {
    /// Magic number for validation (0x47444D4D = "GDMM")
    pub magic: u32,

    /// Manifest format version (for future compatibility)
    pub version: u32,

    /// Next node ID to allocate
    pub next_node_id: u64,

    /// Next edge ID to allocate
    pub next_edge_id: u64,

    /// LSN of the last checkpoint (for WAL replay)
    pub checkpoint_lsn: u64,

    /// Last committed transaction ID
    pub last_committed_tx: u64,
}

impl Manifest {
    /// Create a new manifest with default values.
    pub fn new() -> Self {
        Self {
            magic: MANIFEST_MAGIC,
            version: MANIFEST_VERSION,
            next_node_id: 0,
            next_edge_id: 0,
            checkpoint_lsn: 0,
            last_committed_tx: 0,
        }
    }

    /// Validate the manifest magic number and version.
    pub fn validate(&self) -> anyhow::Result<()> {
        if self.magic != MANIFEST_MAGIC {
            anyhow::bail!(
                "Invalid manifest magic: expected 0x{:08X}, got 0x{:08X}",
                MANIFEST_MAGIC,
                self.magic
            );
        }

        if self.version > MANIFEST_VERSION {
            anyhow::bail!(
                "Unsupported manifest version: {} (supported: {})",
                self.version,
                MANIFEST_VERSION
            );
        }

        Ok(())
    }
}

impl Default for Manifest {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_manifest_new() {
        let manifest = Manifest::new();
        assert_eq!(manifest.magic, MANIFEST_MAGIC);
        assert_eq!(manifest.version, MANIFEST_VERSION);
        assert_eq!(manifest.next_node_id, 0);
        assert_eq!(manifest.next_edge_id, 0);
        assert_eq!(manifest.checkpoint_lsn, 0);
        assert_eq!(manifest.last_committed_tx, 0);
    }

    #[test]
    fn test_manifest_validate_valid() {
        let manifest = Manifest::new();
        assert!(manifest.validate().is_ok());
    }

    #[test]
    fn test_manifest_validate_invalid_magic() {
        let mut manifest = Manifest::new();
        manifest.magic = 0xDEADBEEF;
        assert!(manifest.validate().is_err());
    }

    #[test]
    fn test_manifest_validate_unsupported_version() {
        let mut manifest = Manifest::new();
        manifest.version = MANIFEST_VERSION + 1;
        assert!(manifest.validate().is_err());
    }
}
