// TODO(v5): remove entire test module
#![allow(deprecated)]

#[macro_use]
extern crate serde_json;

use common::*;
use dittolive_ditto::store::batch::ScopedStore;

mod common;

#[test]
fn exec_with_query_args() {
    let ditto = common::get_builder_ditto(None).unwrap();
    exec_with_query_args_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    exec_with_query_args_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    exec_with_query_args_impl(ditto);
}

fn exec_with_query_args_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let store = ditto.store();
    let collection_name = "test";
    let collection = store.collection(collection_name).unwrap();

    // Insert some documents that will match, and some that won't
    collection
        .upsert(json!({"_id": "honda", "make": "Honda", "color": "red"}))
        .unwrap();
    collection
        .upsert(json!({"_id": "ford", "make": "Ford", "color": "blue"}))
        .unwrap();
    collection
        .upsert(json!({"_id": "aston_martin", "make": "Aston Martin", "color": "silver"}))
        .unwrap();
    collection
        .upsert(json!({"_id": "ferrari", "make": "Ferrari", "color": "red"}))
        .unwrap();

    let _ = store.with_batched_write(|mut tx: ScopedStore<'_>| {
        let mut col_tx = tx.collection(collection_name);

        let docs = col_tx
            .find_with_args("color == $args.color", json!({"color": "red"}))
            .exec()
            .unwrap();

        assert_eq!(docs.len(), 2);
        assert_eq!(docs[0].get::<String>("make").unwrap(), "Honda");
        assert_eq!(docs[1].get::<String>("make").unwrap(), "Ferrari");

        tx.commit_changes()
    });
}

#[test]
fn evict_and_remove_with_args() {
    let ditto = common::get_builder_ditto(None).unwrap();
    evict_and_remove_with_args_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    evict_and_remove_with_args_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    evict_and_remove_with_args_impl(ditto);
}

fn evict_and_remove_with_args_impl(ditto: dittolive_ditto::prelude::Ditto) {
    let store = ditto.store();
    let collection_name = "test";
    let collection = store.collection(collection_name).unwrap();
    let honda_id = collection
        .upsert(json!({"make": "Honda", "color": "red"}))
        .unwrap();
    collection
        .upsert(json!({"make": "Ford", "color": "blue"}))
        .unwrap();
    let ferrari_id = collection
        .upsert(json!({"make": "Ferrari", "color": "red"}))
        .unwrap();
    let lamborghini_id = collection
        .upsert(json!({"make": "Lamborghini", "color": "yellow"}))
        .unwrap();

    let docs = collection
        .find_with_args(
            "contains([$args.color1, $args.color2], color)",
            json!({"color1": "red", "color2": "yellow"}),
        )
        .exec()
        .unwrap();
    assert_eq!(docs.len(), 3);

    let _ = store.with_batched_write(|mut tx: ScopedStore<'_>| {
        let mut col_tx = tx.collection(collection_name);

        //@ditto/snippet-start remove-query
        let removed_ids = col_tx
            .find_with_args("color == $args.color", json!({"color": "yellow"}))
            .remove()
            .unwrap();
        //@ditto/snippet-end
        assert_eq!(removed_ids.len(), 1);
        assert_eq!(removed_ids[0], lamborghini_id);

        let sort_param = ffi_sdk::COrderByParam {
            query_c_str: c!("make"),
            direction: ffi_sdk::QuerySortDirection::Descending,
        };
        let evicted_ids = col_tx
            .find_with_args("$args.color == color", json!({"color": "red"}))
            .sort(vec![sort_param])
            .evict()
            .unwrap();
        assert_eq!(evicted_ids.len(), 2);
        assert_eq!(evicted_ids[0], honda_id);
        assert_eq!(evicted_ids[1], ferrari_id);

        let docs = col_tx
            .find_with_args(
                "contains([$args.color1, $args.color2], color)",
                json!({"color1": "red", "color2": "yellow"}),
            )
            .exec()
            .unwrap();
        assert_eq!(docs.len(), 0);

        tx.commit_changes()
    });

    let docs = collection
        .find_with_args(
            "contains([$args.color1, $args.color2], color)",
            json!({"color1": "red", "color2": "yellow"}),
        )
        .exec()
        .unwrap();
    assert_eq!(docs.len(), 0);
}

#[test]
fn repeated_reads_of_the_same_document_in_a_transaction_block_before_and_after_update() {
    let ditto = common::get_builder_ditto(None).unwrap();
    repeated_reads_of_the_same_document_in_a_transaction_block_before_and_after_update_impl(ditto);

    let ditto = common::get_revamp_ditto(None).unwrap();
    repeated_reads_of_the_same_document_in_a_transaction_block_before_and_after_update_impl(ditto);

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    repeated_reads_of_the_same_document_in_a_transaction_block_before_and_after_update_impl(ditto);
}

fn repeated_reads_of_the_same_document_in_a_transaction_block_before_and_after_update_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) {
    let store = ditto.store();
    let collection_name = "test";
    let collection = store.collection(collection_name).unwrap();

    // Insert a document
    let doc_id = collection.upsert(json!({"a": 2})).unwrap();

    store
        .with_batched_write(|mut txn: ScopedStore<'_>| {
            let mut txn_coll = txn.collection(collection_name);

            // Read the document before update
            let doc_before = txn_coll.find_by_id(doc_id.clone()).exec().unwrap();
            assert_eq!(doc_before.get::<i64>("a").unwrap(), 2);

            // Update the document
            txn_coll
                .find_by_id(doc_id.clone())
                .update(|doc| {
                    doc.unwrap().set("a", 99).unwrap();
                })
                .unwrap();

            // Read the document after update
            let doc_after = txn_coll.find_by_id(doc_id.clone()).exec().unwrap();
            assert_eq!(doc_after.get::<i64>("a").unwrap(), 99);

            txn.commit_changes()
        })
        .unwrap();

    // Read the document after the transaction has completed
    let final_doc = collection.find_by_id(doc_id).exec().unwrap();
    assert_eq!(final_doc.get::<i64>("a").unwrap(), 99);
}
