//! WAL file format definitions
//!
//! Defines the binary format for WAL files including headers, entries, and footers.

use serde::{Serialize, Deserialize};
use crate::types::MemoryNode;

/// Magic number for WAL files
pub const WAL_MAGIC: [u8; 4] = [0x57, 0x41, 0x4C, 0x21]; // "WAL!"

/// Current WAL format version
pub const WAL_VERSION: u32 = 1;

/// Header at the start of each WAL file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WALHeader {
    /// Magic number
    pub magic: [u8; 4],
    
    /// Format version
    pub version: u32,
    
    /// Shard ID
    pub shard_id: usize,
    
    /// Sequence number of first entry
    pub start_sequence: u64,
    
    /// File creation timestamp (ms since epoch)
    pub created_at: i64,
}

impl WALHeader {
    /// Create a new WAL header
    pub fn new(shard_id: usize, start_sequence: u64) -> Self {
        use std::time::{SystemTime, UNIX_EPOCH};
        
        Self {
            magic: WAL_MAGIC,
            version: WAL_VERSION,
            shard_id,
            start_sequence,
            created_at: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_millis() as i64,
        }
    }
    
    /// Validate the header
    pub fn validate(&self) -> Result<(), WALFormatError> {
        if self.magic != WAL_MAGIC {
            return Err(WALFormatError::InvalidMagic);
        }
        if self.version > WAL_VERSION {
            return Err(WALFormatError::UnsupportedVersion(self.version));
        }
        Ok(())
    }
    
    /// Get serialized size
    pub fn serialized_size() -> usize {
        // magic(4) + version(4) + shard_id(8) + start_sequence(8) + created_at(8)
        32
    }
}

/// Footer at the end of each WAL file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WALFooter {
    /// Number of entries in this file
    pub entry_count: u32,
    
    /// Sequence number of last entry
    pub end_sequence: u64,
    
    /// Checksum of all entries
    pub checksum: u32,
    
    /// File completion timestamp
    pub completed_at: i64,
}

impl WALFooter {
    /// Create a new WAL footer
    pub fn new(entry_count: u32, end_sequence: u64, checksum: u32) -> Self {
        use std::time::{SystemTime, UNIX_EPOCH};
        
        Self {
            entry_count,
            end_sequence,
            checksum,
            completed_at: SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_millis() as i64,
        }
    }
    
    /// Get serialized size
    pub fn serialized_size() -> usize {
        // entry_count(4) + end_sequence(8) + checksum(4) + completed_at(8)
        24
    }
}

/// Type of WAL entry operation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WALOperation {
    /// Insert a new document
    Insert,
    
    /// Update an existing document
    Update,
    
    /// Delete a document
    Delete,
}

/// A single entry in the WAL
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WALEntry {
    /// Sequence number (globally unique)
    pub sequence: u64,
    
    /// Operation type
    pub operation: WALOperation,
    
    /// Document ID
    pub doc_id: String,
    
    /// Collection name
    pub collection: String,
    
    /// Timestamp when entry was created
    pub timestamp: i64,
    
    /// Document data (None for delete operations)
    pub data: Option<MemoryNode>,
    
    /// Checksum of this entry
    pub checksum: u32,
}

impl WALEntry {
    /// Create a new insert entry
    pub fn insert(sequence: u64, collection: String, node: MemoryNode) -> Self {
        let mut entry = Self {
            sequence,
            operation: WALOperation::Insert,
            doc_id: node.id.clone(),
            collection,
            timestamp: current_timestamp(),
            data: Some(node),
            checksum: 0,
        };
        entry.checksum = entry.calculate_checksum();
        entry
    }
    
    /// Create a new update entry
    pub fn update(sequence: u64, collection: String, node: MemoryNode) -> Self {
        let mut entry = Self {
            sequence,
            operation: WALOperation::Update,
            doc_id: node.id.clone(),
            collection,
            timestamp: current_timestamp(),
            data: Some(node),
            checksum: 0,
        };
        entry.checksum = entry.calculate_checksum();
        entry
    }
    
    /// Create a new delete entry
    pub fn delete(sequence: u64, collection: String, doc_id: String) -> Self {
        let mut entry = Self {
            sequence,
            operation: WALOperation::Delete,
            doc_id,
            collection,
            timestamp: current_timestamp(),
            data: None,
            checksum: 0,
        };
        entry.checksum = entry.calculate_checksum();
        entry
    }
    
    /// Calculate checksum for this entry
    pub fn calculate_checksum(&self) -> u32 {
        let mut hasher = crc32fast::Hasher::new();
        hasher.update(&self.sequence.to_le_bytes());
        hasher.update(&[self.operation as u8]);
        hasher.update(self.doc_id.as_bytes());
        hasher.update(self.collection.as_bytes());
        hasher.update(&self.timestamp.to_le_bytes());
        
        if let Some(ref data) = self.data {
            if let Ok(bytes) = serde_json::to_vec(data) {
                hasher.update(&bytes);
            }
        }
        
        hasher.finalize()
    }
    
    /// Verify checksum
    pub fn verify_checksum(&self) -> bool {
        let expected = self.calculate_checksum();
        expected == self.checksum
    }
    
    /// Serialize to bytes
    pub fn to_bytes(&self) -> Result<Vec<u8>, WALFormatError> {
        serde_json::to_vec(self)
            .map_err(|e| WALFormatError::SerializationError(e.to_string()))
    }
    
    /// Deserialize from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, WALFormatError> {
        serde_json::from_slice(bytes)
            .map_err(|e| WALFormatError::DeserializationError(e.to_string()))
    }
}

/// Get current timestamp in milliseconds
fn current_timestamp() -> i64 {
    use std::time::{SystemTime, UNIX_EPOCH};
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_millis() as i64
}

/// Errors related to WAL format
#[derive(Debug, thiserror::Error)]
pub enum WALFormatError {
    #[error("Invalid magic number")]
    InvalidMagic,
    
    #[error("Unsupported WAL version: {0}")]
    UnsupportedVersion(u32),
    
    #[error("Checksum mismatch")]
    ChecksumMismatch,
    
    #[error("Serialization error: {0}")]
    SerializationError(String),
    
    #[error("Deserialization error: {0}")]
    DeserializationError(String),
    
    #[error("Truncated entry")]
    TruncatedEntry,
    
    #[error("Invalid entry length: {0}")]
    InvalidLength(usize),
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_wal_header() {
        let header = WALHeader::new(0, 100);
        assert!(header.validate().is_ok());
    }
    
    #[test]
    fn test_wal_entry_checksum() {
        let node = MemoryNode::new("test content");
        let entry = WALEntry::insert(1, "default".to_string(), node);
        
        // Checksum should be set
        assert_ne!(entry.checksum, 0);
        
        // Verification should pass
        assert!(entry.verify_checksum());
    }
    
    #[test]
    fn test_wal_entry_serialization() {
        let node = MemoryNode::new("test content");
        let entry = WALEntry::insert(1, "default".to_string(), node);
        
        // Serialize
        let bytes = entry.to_bytes().unwrap();
        
        // Deserialize
        let recovered = WALEntry::from_bytes(&bytes).unwrap();
        
        assert_eq!(entry.sequence, recovered.sequence);
        assert_eq!(entry.doc_id, recovered.doc_id);
        assert!(recovered.verify_checksum());
    }
}
