//! Transaction API for writing to Iceberg tables

use crate::spec::DataFile;
use crate::table::Table;

/// Operations that can be performed in a transaction
#[derive(Debug, Clone)]
pub enum TransactionOperation {
    /// Append data files
    Append(Vec<DataFile>),
    /// Rewrite files: atomically delete old files and add new ones.
    /// Used for compaction, where we replace N small files with M larger files.
    Rewrite {
        /// Files to be deleted (marked as deleted in manifest)
        files_to_delete: Vec<DataFile>,
        /// New files to add (marked as added in manifest)
        files_to_add: Vec<DataFile>,
    },
}

/// A transaction for modifying a table
pub struct Transaction {
    table: Table,
    operations: Vec<TransactionOperation>,
}

impl Transaction {
    /// Create a new transaction
    pub(crate) fn new(table: Table) -> Self {
        Self {
            table,
            operations: Vec::new(),
        }
    }

    /// Get the table this transaction operates on
    pub fn table(&self) -> &Table {
        &self.table
    }

    /// Append data files to the table
    pub fn append(mut self, data_files: Vec<DataFile>) -> Self {
        self.operations
            .push(TransactionOperation::Append(data_files));
        self
    }

    /// Rewrite files: atomically delete old files and add new ones.
    /// Used for compaction, where we replace N small files with M larger files.
    pub fn rewrite(mut self, files_to_delete: Vec<DataFile>, files_to_add: Vec<DataFile>) -> Self {
        self.operations.push(TransactionOperation::Rewrite {
            files_to_delete,
            files_to_add,
        });
        self
    }

    /// Check if transaction has any operations
    pub fn has_operations(&self) -> bool {
        !self.operations.is_empty()
    }

    /// Get the operations (for internal use)
    pub(crate) fn operations(&self) -> &[TransactionOperation] {
        &self.operations
    }

    /// Replace the table metadata backing this transaction (for retries)
    pub(crate) fn rebind_table(self, table: Table) -> Self {
        Self {
            table,
            operations: self.operations,
        }
    }

    /// Commit the transaction, writing snapshots to the catalog
    pub async fn commit(
        self,
        catalog: &dyn crate::catalog::Catalog,
        timestamp_ms: i64,
    ) -> crate::error::Result<()> {
        crate::commit::commit_transaction(self, catalog, timestamp_ms).await
    }
}
