use crate::{
    Persy, TransactionId,
    error::{GenericError, PE},
    journal::recover_impl::RecoverImpl,
    persy::PersyImpl,
};
use std::sync::Arc;

/// Possible state of a transaction in the log
#[derive(PartialEq, Eq, Debug, Clone)]
pub enum RecoverStatus {
    /// Can't find the start of the transaction so already completed  
    Partial,
    /// Started but not completed
    Started,
    /// Successfully prepared
    PrepareCommit,
    /// rollback-ed
    Rollback,
    /// Successfully committed after prepared
    Commit,
    /// Successfully cleaned up resources after commit
    Cleanup,
}

/// Intermediate recover status to select witch transactions to commit or rollback and list witch
/// transactions are in a intermediate state
///
/// # Example
///
///
/// ```rust
/// # use persy::{Persy,Config};
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # Persy::create("./target/recover_example.persy")?;
/// let mut recover = Persy::recover("./target/recover_example.persy", Config::new())?;
/// for (tx_id,status) in recover.list_transactions() {
///     // Check the transaction if can be committed using the tx_id
///     if true {
///         // if so commit the tx
///         recover.commit(tx_id);
///     } else {
///         // otherwise roll back it
///         recover.rollback(tx_id);
///     }
///     // finalize all the transaction marked to finalize and get a persy instance.
/// }
/// let persy = recover.finalize()?;
/// # std::fs::remove_file("./target/recover_example.persy")?;
/// # Ok(())
/// # }
/// ```
pub struct Recover {
    recover_impl: RecoverImpl,
    persy_impl: Arc<PersyImpl>,
}

impl Recover {
    pub(crate) fn new(recover_impl: RecoverImpl, persy_impl: Arc<PersyImpl>) -> Recover {
        Recover {
            recover_impl,
            persy_impl,
        }
    }
    /// List all the transactions found in the log with the current status
    pub fn list_transactions(&self) -> Vec<(TransactionId, RecoverStatus)> {
        self.recover_impl.list_transactions()
    }
    /// Mark to commit a transaction in the log with state prepared commit
    pub fn commit(&mut self, tx_id: TransactionId) {
        self.recover_impl.commit(tx_id)
    }
    /// Mark to rollback a transaction that is not yet committed
    pub fn rollback(&mut self, tx_id: TransactionId) {
        self.recover_impl.rollback(tx_id)
    }
    /// Read the status of a transaction in the log
    pub fn status(&self, tx_id: TransactionId) -> Option<RecoverStatus> {
        self.recover_impl.status(tx_id)
    }
    /// Recover all the prepared committed transactions that are not marked to rollback
    pub fn finalize(self) -> Result<Persy, PE<GenericError>> {
        self.persy_impl.final_recover(self.recover_impl)?;
        Ok(Persy {
            persy_impl: self.persy_impl,
        })
    }
}
