//! The Live Query "Everything" Test
//! extracted into its own file (compilation unit)
//! for easier debuging

use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc, Mutex,
};

use common::*;
use dittolive_ditto::dql::QueryResult;
use serde_json::json;
mod common;

#[test]
fn everything() -> Result<(), Box<dyn std::error::Error>> {
    let ditto = common::get_builder_ditto(None).unwrap();
    everything_impl(ditto)?;

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

    let ditto = common::get_revamp_ditto_async_sync(None).unwrap();
    everything_impl(ditto)?;

    Ok(())
}

fn everything_impl(
    ditto: dittolive_ditto::prelude::Ditto,
) -> Result<(), Box<dyn std::error::Error>> {
    let store = ditto.store();
    let uuid1 = format!(
        "collection_{}",
        uuid::Uuid::new_v4().to_string().replace('-', "_")
    );
    let col1_name = uuid1.clone();
    let uuid2 = format!(
        "collection_{}",
        uuid::Uuid::new_v4().to_string().replace('-', "_")
    );
    let col2_name = uuid2.clone();

    let doc1 = TestType {
        make: String::from("Honda"),
        color: TestColor::Red,
        ..Default::default()
    };

    let insert_result = futures::executor::block_on(store.execute_v2((
        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name),
        json!({"doc": doc1}),
    )))?;
    let redhonda_id = Arc::new(Mutex::new(insert_result.mutated_document_ids()[0].clone()));

    let doc2 = TestType {
        make: String::from("Honda"),
        color: TestColor::Crimson,
        ..Default::default()
    };

    let _crimsonhonda_id = Arc::new(
        futures::executor::block_on(store.execute_v2((
            format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name),
            json!({"doc": doc2}),
        )))?
        .mutated_document_ids()[0]
            .clone(),
    );

    let doc3 = TestType {
        make: String::from("Bentley"),
        color: TestColor::Red,
        ..Default::default()
    };
    let doc4 = TestType {
        make: String::from("Jaguar"),
        color: TestColor::Red,
        ..Default::default()
    };

    let bentley_result = futures::executor::block_on(store.execute_v2((
        format!("INSERT INTO {} DOCUMENTS (:doc)", col2_name),
        json!({"doc": doc3}),
    )))?;
    let redbentley_id = Arc::new(Mutex::new(bentley_result.mutated_document_ids()[0].clone()));

    let jaguar_result = futures::executor::block_on(store.execute_v2((
        format!("INSERT INTO {} DOCUMENTS (:doc)", col2_name),
        json!({"doc": doc4}),
    )))?;
    let redjaguar_id = Arc::new(Mutex::new(jaguar_result.mutated_document_ids()[0].clone()));

    let redtoyota_id = Arc::new(Mutex::new(DocumentId::default()));
    let redford_id = Arc::new(Mutex::new(DocumentId::default()));
    let orangemazda_id = Arc::new(Mutex::new(DocumentId::default()));
    let yellowam_id = Arc::new(Mutex::new(DocumentId::default()));
    let redchevy_id = Arc::new(Mutex::new(DocumentId::default()));
    let redhyundai_id = Arc::new(Mutex::new(DocumentId::default()));
    let pinkjeep_id = Arc::new(Mutex::new(DocumentId::default()));
    let blackbmw_id = Arc::new(Mutex::new(DocumentId::default()));
    let redferrari_id = Arc::new(Mutex::new(DocumentId::default()));

    let counter = Arc::new(Mutex::new(0));
    let col1_name_clone = col1_name.clone();
    let col2_name_clone = col2_name.clone();

    let store_copy = store.clone();

    let is_complete = Arc::new(AtomicBool::new(false));
    let is_complete_copy = Arc::clone(&is_complete);

    let handler =
        move |result: QueryResult| {
            if let Ok(mut counter) = counter.lock() {
                *counter += 1;
            }

            let count = *counter.lock().unwrap();
            match count {
                1 => {
                    // Initial state - should have 1 red honda
                    assert_eq!(result.item_count(), 1);
                    let first_item = result.get_item(0).unwrap();
                    let first_doc: TestType = first_item.deserialize_value().unwrap();
                    assert_eq!(first_doc.make, "Honda");
                    assert_eq!(first_doc.color, TestColor::Red);

                    let redtoyota = TestType {
                        color: TestColor::Red,
                        make: String::from("Toyota"),
                        ..Default::default()
                    };
                    let toyota_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": redtoyota}),
                    )))
                    .unwrap();
                    *redtoyota_id.lock().unwrap() = toyota_result.mutated_document_ids()[0].clone();
                }
                2 => {
                    // Should have 2 red cars now (Honda + Toyota)
                    assert_eq!(result.item_count(), 2);

                    let redford = TestType {
                        make: String::from("Ford"),
                        color: TestColor::Red,
                        ..Default::default()
                    };
                    let ford_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": redford}),
                    )))
                    .unwrap();
                    *redford_id.lock().unwrap() = ford_result.mutated_document_ids()[0].clone();
                }
                3 => {
                    // Should have 3 red cars now (Honda + Toyota + Ford)
                    assert_eq!(result.item_count(), 3);

                    // Make the red toyota a red Maserati
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET make = :make WHERE _id = :id",
                            col1_name_clone
                        ),
                        json!({"make": "Maserati", "id": redtoyota_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();
                }
                4 => {
                    // Still should have 3 red cars (Honda + Maserati + Ford)
                    assert_eq!(result.item_count(), 3);

                    let orangemazda = TestType {
                        make: String::from("Mazda"),
                        color: TestColor::Orange,
                        ..Default::default()
                    };
                    let yellowam = TestType {
                        make: String::from("Aston Martin"),
                        color: TestColor::Yellow,
                        ..Default::default()
                    };
                    let redchevy = TestType {
                        make: String::from("Chevrolet"),
                        color: TestColor::Red,
                        ..Default::default()
                    };

                    let mazda_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": orangemazda}),
                    )))
                    .unwrap();
                    *orangemazda_id.lock().unwrap() =
                        mazda_result.mutated_document_ids()[0].clone();

                    let am_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": yellowam}),
                    )))
                    .unwrap();
                    *yellowam_id.lock().unwrap() = am_result.mutated_document_ids()[0].clone();

                    let chevy_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": redchevy}),
                    )))
                    .unwrap();
                    *redchevy_id.lock().unwrap() = chevy_result.mutated_document_ids()[0].clone();
                }
                5 => {
                    // Should have 4 red cars now (Honda + Maserati + Ford + Chevy)
                    assert_eq!(result.item_count(), 4);

                    // make the red honda blue (removes it from our red query results)
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                    format!("UPDATE {} SET color = :color WHERE _id = :id", col1_name_clone),
                    json!({"color": TestColor::Blue, "id": redhonda_id.lock().unwrap().clone()})
                ))).unwrap();
                }
                6 => {
                    // Should have 3 red cars now (Maserati + Ford + Chevy), Honda is now blue
                    assert_eq!(result.item_count(), 3);

                    // Perform batch operations using multiple execute_v2 calls
                    let redhyundai = TestType {
                        make: String::from("Hyundai"),
                        color: TestColor::Red,
                        ..Default::default()
                    };
                    let pinkjeep = TestType {
                        make: String::from("Jeep"),
                        color: TestColor::Pink,
                        ..Default::default()
                    };

                    let hyundai_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": redhyundai}),
                    )))
                    .unwrap();
                    *redhyundai_id.lock().unwrap() =
                        hyundai_result.mutated_document_ids()[0].clone();

                    let jeep_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": pinkjeep}),
                    )))
                    .unwrap();
                    *pinkjeep_id.lock().unwrap() = jeep_result.mutated_document_ids()[0].clone();

                    // remove the red toyota (now maserati)
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!("DELETE FROM {} WHERE _id = :id", col1_name_clone),
                        json!({"id": redtoyota_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();

                    // remove the orange mazda
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!("DELETE FROM {} WHERE _id = :id", col1_name_clone),
                        json!({"id": orangemazda_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();

                    // make the Ford an audi
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET make = :make WHERE _id = :id",
                            col1_name_clone
                        ),
                        json!({"make": "Audi", "id": redford_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();

                    // make the chevy white (removes it from red results)
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                    format!("UPDATE {} SET color = :color WHERE _id = :id", col1_name_clone),
                    json!({"color": TestColor::White, "id": redchevy_id.lock().unwrap().clone()})
                ))).unwrap();

                    // make the aston martin red (adds it to red results)
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET color = :color WHERE _id = :id",
                            col1_name_clone
                        ),
                        json!({"color": TestColor::Red, "id": yellowam_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();

                    // operations in the second collection
                    let blackbmw = TestType {
                        make: String::from("BMW"),
                        color: TestColor::Black,
                        ..Default::default()
                    };
                    let bmw_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col2_name_clone),
                        json!({"doc": blackbmw}),
                    )))
                    .unwrap();
                    *blackbmw_id.lock().unwrap() = bmw_result.mutated_document_ids()[0].clone();

                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!("DELETE FROM {} WHERE _id = :id", col2_name_clone),
                        json!({"id": redbentley_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();

                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET make = :make WHERE _id = :id",
                            col2_name_clone
                        ),
                        json!({"make": "Citroen", "id": redjaguar_id.lock().unwrap().clone()}),
                    )))
                    .unwrap();
                }
                7 => {
                    // Should have 3 red cars (Audi, Hyundai, Aston Martin)
                    assert_eq!(
                        result.item_count(),
                        3,
                        "Results: {:?}",
                        result.iter().map(|x| x.json_string()).collect::<Vec<_>>()
                    );

                    let redferrari = TestType {
                        make: String::from("Ferrari"),
                        color: TestColor::Red,
                        ..Default::default()
                    };
                    let ferrari_result = futures::executor::block_on(store_copy.execute_v2((
                        format!("INSERT INTO {} DOCUMENTS (:doc)", col1_name_clone),
                        json!({"doc": redferrari}),
                    )))
                    .unwrap();
                    *redferrari_id.lock().unwrap() =
                        ferrari_result.mutated_document_ids()[0].clone();
                }
                8 => {
                    // Should have 4 red cars now (Audi, Hyundai, Aston Martin, Ferrari)
                    assert_eq!(result.item_count(), 4);

                    // find the red ferrari, make it a porsche, then make it brown, then delete it
                    let id = redferrari_id.lock().unwrap().clone();
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET make = :make WHERE _id = :id",
                            col1_name_clone
                        ),
                        json!({"make": "Porsche", "id": id}),
                    )))
                    .unwrap();
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!(
                            "UPDATE {} SET color = :color WHERE _id = :id",
                            col1_name_clone
                        ),
                        json!({"color": TestColor::Brown, "id": id}),
                    )))
                    .unwrap();
                    let _ = futures::executor::block_on(store_copy.execute_v2((
                        format!("DELETE FROM {} WHERE _id = :id", col1_name_clone),
                        json!({"id": id}),
                    )))
                    .unwrap();
                }
                9 => {
                    // Should have 3 red cars now (Audi, Hyundai, Aston Martin)
                    assert_eq!(result.item_count(), 3);
                    is_complete_copy.store(true, Ordering::SeqCst);
                }
                _ => {
                    panic!("Unexpected count {}", count);
                }
            }
        };

    let lq = store
        .register_observer_v2(
            (
                format!("SELECT * FROM {} WHERE color = :color", col1_name),
                json!({"color": TestColor::Red}),
            ),
            handler,
        )
        .unwrap();

    while !is_complete.load(Ordering::SeqCst) {}
    drop(lq);
    Ok(())
}
