//! HNSW Index transaction support
//!
//! Contains transaction methods for atomic operations with rollback support.

use std::sync::atomic::Ordering as AtomicOrdering;

use super::index::HNSWIndex;
use super::types::{
    Operation, Transaction, TransactionState, Savepoint, HNSWError,
};

impl HNSWIndex {
    // ============================================================================
    // P0-3: Transaction Methods
    // ============================================================================
    
    /// Begin a new transaction
    /// 
    /// All subsequent insert/delete operations will be recorded and can be
    /// rolled back until commit() is called.
    pub fn begin_transaction(&self) -> Result<u64, HNSWError> {
        let mut tx = self.active_transaction.write();
        if tx.is_some() {
            return Err(HNSWError::TransactionActive);
        }
        
        let tx_id = self.transaction_counter.fetch_add(1, AtomicOrdering::SeqCst);
        *tx = Some(Transaction::new(tx_id));
        self.savepoints.write().clear();
        
        Ok(tx_id)
    }
    
    /// Commit the current transaction
    /// 
    /// All operations in the transaction become permanent.
    pub fn commit(&self) -> Result<(), HNSWError> {
        let mut tx = self.active_transaction.write();
        match tx.as_mut() {
            Some(t) if t.is_active() => {
                t.state = TransactionState::Committed;
                *tx = None;
                self.savepoints.write().clear();
                Ok(())
            }
            Some(_) => Err(HNSWError::TransactionNotActive),
            None => Err(HNSWError::NoActiveTransaction),
        }
    }
    
    /// Rollback the current transaction
    /// 
    /// All operations in the transaction are undone.
    pub fn rollback(&self) -> Result<(), HNSWError> {
        let mut tx = self.active_transaction.write();
        let transaction = match tx.take() {
            Some(t) if t.is_active() => t,
            Some(_) => return Err(HNSWError::TransactionNotActive),
            None => return Err(HNSWError::NoActiveTransaction),
        };
        
        // Undo operations in reverse order
        self.undo_operations(&transaction.operations)?;
        self.savepoints.write().clear();
        
        Ok(())
    }
    
    /// Create a savepoint within the current transaction
    pub fn savepoint(&self, name: impl Into<String>) -> Result<(), HNSWError> {
        let tx = self.active_transaction.read();
        let transaction = tx.as_ref()
            .ok_or(HNSWError::NoActiveTransaction)?;
        
        if !transaction.is_active() {
            return Err(HNSWError::TransactionNotActive);
        }
        
        let savepoint = Savepoint {
            name: name.into(),
            operation_index: transaction.operations.len(),
        };
        
        self.savepoints.write().push(savepoint);
        Ok(())
    }
    
    /// Rollback to a savepoint
    /// 
    /// All operations after the savepoint are undone.
    pub fn rollback_to_savepoint(&self, name: &str) -> Result<(), HNSWError> {
        let savepoint_index = {
            let savepoints = self.savepoints.read();
            savepoints.iter()
                .position(|s| s.name == name)
                .ok_or_else(|| HNSWError::SavepointNotFound(name.to_string()))?
        };
        
        let ops_to_undo = {
            let mut tx = self.active_transaction.write();
            let transaction = tx.as_mut()
                .ok_or(HNSWError::NoActiveTransaction)?;
            
            if !transaction.is_active() {
                return Err(HNSWError::TransactionNotActive);
            }
            
            let savepoints = self.savepoints.read();
            let savepoint = &savepoints[savepoint_index];
            let op_idx = savepoint.operation_index;
            
            // Get operations to undo
            let ops: Vec<Operation> = transaction.operations[op_idx..].to_vec();
            
            // Truncate transaction operations
            transaction.operations.truncate(op_idx);
            
            ops
        };
        
        // Undo operations in reverse order
        self.undo_operations(&ops_to_undo)?;
        
        // Remove savepoints after this one
        let mut savepoints = self.savepoints.write();
        savepoints.truncate(savepoint_index + 1);
        
        Ok(())
    }
    
    /// Check if there's an active transaction
    pub fn in_transaction(&self) -> bool {
        self.active_transaction.read()
            .as_ref()
            .map(|t| t.is_active())
            .unwrap_or(false)
    }
    
    /// Get current transaction ID
    pub fn current_transaction_id(&self) -> Option<u64> {
        self.active_transaction.read()
            .as_ref()
            .filter(|t| t.is_active())
            .map(|t| t.id)
    }
    
    /// Undo a list of operations in reverse order
    pub(crate) fn undo_operations(&self, operations: &[Operation]) -> Result<(), HNSWError> {
        for op in operations.iter().rev() {
            match op {
                Operation::Insert { external_id, internal_id, .. } => {
                    // Undo insert: mark as deleted and remove from id_map
                    self.deleted.write().insert(*internal_id);
                    self.id_map.write().remove(external_id);
                }
                Operation::Delete { internal_id, .. } => {
                    // Undo delete: restore the node
                    self.deleted.write().remove(internal_id);
                }
                Operation::Restore { internal_id, .. } => {
                    // Undo restore: delete the node again
                    self.deleted.write().insert(*internal_id);
                }
            }
        }
        Ok(())
    }
}
