//! Use [`ditto.store().with_batched_write(...)`] to begin a batched write operation.
//!
//! [`ditto.store().with_batched_write(...)`]: crate::store::Store::with_batched_write

#![allow(deprecated)]

use_prelude!();

use collection::ScopedCollection;

use super::collection::document_id::DocumentId;
mod collection;

/// Use lifetime parameters as type-level IDs (generative lifetimes),
/// so as to prevent the following anti-pattern from compiling:
///
/// ```no_run
/// use dittolive_ditto::prelude::*;
/// use std::path::Path;
/// use serde_json::json;
///
/// let dir1 = Path::new("...");
/// let mut ditto1 = Ditto::builder().build().unwrap();
///
/// ditto1.store().with_batched_write(|mut tx| {
///     // API requires that this closure return:
///     // - either `tx.commit_changes()`
///     // - or `tx.revert_changes()`
///     let mut coll = tx.collection("collection");
///     coll
///        .find_with_args("color == $args.color", json!({"color": "yellow"}))
///        .remove()
///        .unwrap();
///     tx.commit_changes()
/// });
/// ```
use marker::InvariantLifetime as TypeLevelId;

/// Use [`.with_batched_write(...)`] to group operations in a transaction.
///
/// # Example
///
/// ```
/// use dittolive_ditto::prelude::*;
///
/// # fn example(ditto: &Ditto) -> anyhow::Result<()> {
/// ditto.store().with_batched_write(|mut batch| {
///     let mut foo_collection = batch.collection("foo");
///     foo_collection.find_by_id((&b"fooey"[..]).into()).remove();
///
///     // Expensive multi-mutation op:
///     let mut bar_collection = batch.collection("bar");
///     for i in 0..10_000 {
///         bar_collection.upsert(serde_json::json!({
///             "bar_count": i
///         })).unwrap();
///     }
///
///     // At this point, we must say whether we commit or revert
///     // these changes:
///     batch.commit_changes()
/// })?;
/// # Ok(())
/// # }
/// ```
///
/// [`.with_batched_write(...)`]: crate::store::Store::with_batched_write
#[deprecated(
    since = "4.14.4",
    note = "Will be removed in SDK 5.0. Use DQL queries instead"
)]
pub struct ScopedStore<'batch> {
    // " 'batch == 'txn "
    txn: &'batch mut ffi_sdk::CWriteTransaction,

    // This could be 'store (which is ≥ 'txn), but for all effects and purposes
    // shrinking this to `'txn` is just as useful, and simplifies / reduces
    // the number of lifetime parameters.
    store: &'batch Store, // a Ref to the REAL store not the scoped store

    results: &'batch mut Vec<WriteTransactionResult>,

    // make sure the `'batch` lifetime is invariant so that it can be used as
    // a type-level unique id.
    _txn_id: TypeLevelId<'batch>,
}

pub(crate) fn with_batched_write<F>(
    store: &Store, // Has lifetime of 'store
    f: F,
) -> Result<Vec<WriteTransactionResult>, crate::error::DittoError>
where
    for<'batch> F: FnOnce(ScopedStore<'batch>) -> Action<'batch>,
{
    use super::*;

    let hint: Option<char_p::Ref<'_>> = None;
    let mut txn = ffi_sdk::ditto_write_transaction(&*store.ditto, hint).ok()?;
    let mut results = vec![];

    let batch = ScopedStore {
        txn: &mut txn,
        store, // The &Store contains an Arc to the root Ditto instance
        results: &mut results,
        _txn_id: TypeLevelId::default(),
    };
    match f(batch).0 {
        ActionKind::Commit => {
            let status = ffi_sdk::ditto_write_transaction_commit(&*store.ditto, txn);
            if status != 0 {
                return Err(DittoError::from_ffi(ErrorKind::Internal));
            }
        }
        ActionKind::Rollback => {
            ffi_sdk::ditto_write_transaction_rollback(&*store.ditto, txn);
        }
    }
    Ok(results)
}

/// Use [`.commit_changes()`] or [`.revert_changes()`] in [`.with_batch_write(...)`].
///
/// An `Action` refers to the action to be taken at the end of a batched write (transaction).
/// A batch may end by being committed or reverted.
///
/// [`.commit_changes()`]: crate::store::query_builder::ScopedStore::commit_changes
/// [`.revert_changes()`]: crate::store::query_builder::ScopedStore::revert_changes
/// [`.with_batch_write(...)`]: crate::store::Store::with_batched_write
#[deprecated(
    since = "4.14.4",
    note = "Will be removed in SDK 5.0. Use DQL queries instead"
)]
pub struct Action<'batch>(ActionKind, TypeLevelId<'batch>);

enum ActionKind {
    Commit,
    Rollback,
}

impl<'batch> ScopedStore<'batch> {
    /// Commit changes done in this ScopedStore.
    pub fn commit_changes(self: ScopedStore<'batch>) -> Action<'batch> {
        Action(ActionKind::Commit, TypeLevelId::default())
    }

    /// Revert changes done in this ScopedStore.
    pub fn revert_changes(self: ScopedStore<'batch>) -> Action<'batch> {
        Action(ActionKind::Rollback, TypeLevelId::default())
    }

    /// Return an handle to change a single [`Collection`].
    pub fn collection<'coll>(
        self: &'coll mut ScopedStore<'batch>,
        collection_name: &'_ str,
    ) -> ScopedCollection<'coll, 'batch> {
        let c_name = char_p::new(collection_name);
        ScopedCollection {
            batch: self,
            collection_name: c_name,
        }
    }
}

/// Returned from [`.with_batched_write(...)`] and describes the operations performed in the batch.
///
/// Provides information about the result of an operation on a document
/// that was part of a write transaction.
///
/// [`.with_batched_write(...)`]: crate::store::Store::with_batched_write
#[deprecated(
    since = "4.14.4",
    note = "Will be removed in SDK 5.0. Use DQL queries instead"
)]
pub struct WriteTransactionResult {
    /// The name of the collection that the operation took place in.
    pub collection_name: char_p::Box,

    /// The Id of the document that the write transaction result applies to.
    pub doc_id: DocumentId,

    /// The type of write transaction result.
    pub kind: DocChangeKind,
}

#[derive(Debug, PartialEq, Eq)]

/// The types of write transaction result.
#[deprecated(
    since = "4.14.4",
    note = "Will be removed in SDK 5.0. Use DQL queries instead"
)]
pub enum DocChangeKind {
    Inserted,
    Removed,
    Evicted,
    Updated,
}
