// Copyright 2024 The Drasi Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;

use async_trait::async_trait;

use drasi_core::{
    in_memory_index::in_memory_element_index::InMemoryElementIndex, query::QueryBuilder,
};

use crate::QueryTestConfig;

struct InMemoryQueryConfig {}

impl InMemoryQueryConfig {
    pub fn new() -> Self {
        InMemoryQueryConfig {}
    }
}

#[async_trait]
impl QueryTestConfig for InMemoryQueryConfig {
    async fn config_query(&self, builder: QueryBuilder) -> QueryBuilder {
        log::info!("using in memory indexes");
        let mut element_index = InMemoryElementIndex::new();
        element_index.enable_archive();
        let element_index = Arc::new(element_index);

        builder
            .with_element_index(element_index.clone())
            .with_archive_index(element_index.clone())
    }
}

mod building_comfort {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn building_comfort_use_case() {
        let test_config = InMemoryQueryConfig::new();
        building_comfort::building_comfort_use_case(&test_config).await;
    }
}

mod curbside_pickup {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn order_ready_then_vehicle_arrives() {
        let test_config = InMemoryQueryConfig::new();
        curbside_pickup::order_ready_then_vehicle_arrives(&test_config).await;
    }

    #[tokio::test]
    async fn vehicle_arrives_then_order_ready() {
        let test_config = InMemoryQueryConfig::new();
        curbside_pickup::vehicle_arrives_then_order_ready(&test_config).await;
    }

    #[tokio::test]
    async fn vehicle_arrives_then_order_ready_duplicate() {
        let test_config = InMemoryQueryConfig::new();
        curbside_pickup::vehicle_arrives_then_order_ready_duplicate(&test_config).await;
    }
}

mod incident_alert {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn incident_alert() {
        let test_config = InMemoryQueryConfig::new();
        incident_alert::incident_alert(&test_config).await;
    }
}

mod linear_regression {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn linear_gradient() {
        let test_config = InMemoryQueryConfig::new();
        linear_regression::linear_gradient(&test_config).await;
    }
}

mod min_value {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn min_value() {
        let test_config = InMemoryQueryConfig::new();
        min_value::min_value(&test_config).await;
    }
}

mod remap {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn remap() {
        let test_config = InMemoryQueryConfig::new();
        remap::remap(&test_config).await;
    }

    #[tokio::test]
    pub async fn remap_invalid_middleware_config() {
        let test_config = InMemoryQueryConfig::new();
        remap::remap_invalid_config_fails(&test_config).await;
    }

    #[tokio::test]
    pub async fn remap_incorrect_middleware_structure() {
        let test_config = InMemoryQueryConfig::new();
        remap::remap_incorrect_structure_fails(&test_config).await;
    }
}

mod overdue_invoice {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn overdue_invoice() {
        let test_config = InMemoryQueryConfig::new();
        overdue_invoice::overdue_invoice(&test_config).await;
    }

    #[tokio::test]
    pub async fn overdue_count_persistent() {
        let test_config = InMemoryQueryConfig::new();
        overdue_invoice::overdue_count_persistent(&test_config).await;
    }
}

mod sensor_heartbeat {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn not_reported() {
        let test_config = InMemoryQueryConfig::new();
        sensor_heartbeat::not_reported(&test_config).await;
    }

    #[tokio::test]
    pub async fn not_reported_with_true_now_or_later() {
        let test_config = InMemoryQueryConfig::new();
        sensor_heartbeat::not_reported_with_true_now_or_later(&test_config).await;
    }

    #[tokio::test]
    pub async fn percent_not_reported() {
        let test_config = InMemoryQueryConfig::new();
        sensor_heartbeat::percent_not_reported(&test_config).await;
    }
}

mod temporal_retrieval {
    use super::InMemoryQueryConfig;
    use crate::temporal_retrieval::get_version_by_timestamp;
    use crate::temporal_retrieval::get_versions_by_timerange;

    #[tokio::test]
    async fn get_version_by_timestamp() {
        let test_config = InMemoryQueryConfig::new();
        get_version_by_timestamp::get_version_by_timestamp(&test_config).await;
    }

    #[tokio::test]
    async fn get_versions_by_timerange_with_initial_value_flag() {
        let test_config = InMemoryQueryConfig::new();
        get_versions_by_timerange::get_versions_by_timerange_with_initial_value_flag(&test_config)
            .await;
    }

    #[tokio::test]
    async fn get_versions_by_range() {
        let test_config = InMemoryQueryConfig::new();
        get_versions_by_timerange::get_versions_by_timerange(&test_config).await;
    }
}

mod crosses_above_a_threshold {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn crosses_above_a_threshold() {
        let test_config = InMemoryQueryConfig::new();
        crosses_above_a_threshold::crosses_above_a_threshold(&test_config).await;
    }

    #[tokio::test]
    pub async fn crosses_above_a_threshold_with_overdue_days() {
        let test_config = InMemoryQueryConfig::new();
        crosses_above_a_threshold::crosses_above_a_threshold_with_overdue_days(&test_config).await;
    }
}

mod greater_than_a_threshold {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn greater_than_a_threshold() {
        let test_config = InMemoryQueryConfig::new();
        greater_than_a_threshold::greater_than_a_threshold(&test_config).await;
    }

    #[tokio::test]
    pub async fn greater_than_a_threshold_by_customer() {
        let test_config = InMemoryQueryConfig::new();
        greater_than_a_threshold::greater_than_a_threshold_by_customer(&test_config).await;
    }
}

mod crosses_above_and_stays_above {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn crosses_above_and_stays_above() {
        let test_config = InMemoryQueryConfig::new();
        crosses_above_and_stays_above::crosses_above_and_stays_above(&test_config).await;
    }
}

mod crosses_above_three_times_in_an_hour {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn crosses_above_three_times_in_an_hour() {
        let test_config = InMemoryQueryConfig::new();
        crosses_above_three_times_in_an_hour::crosses_above_three_times_in_an_hour(&test_config)
            .await;
    }
}

mod logical_conditions {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn logical_conditions() {
        let test_config = InMemoryQueryConfig::new();
        logical_conditions::logical_conditions(&test_config).await;
    }
}

mod steps_happen_in_any_order {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn steps_happen_in_any_order() {
        let test_config = InMemoryQueryConfig::new();
        steps_happen_in_any_order::steps_happen_in_any_order(&test_config).await;
    }
}

mod rolling_average_decrease_by_ten {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn rolling_average_decrease_by_ten() {
        let test_config = InMemoryQueryConfig::new();
        rolling_average_decrease_by_ten::rolling_average_decrease_by_ten(&test_config).await;
    }
}

mod decreases_by_ten {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn decrease_by_ten() {
        let test_config = InMemoryQueryConfig::new();
        decrease_by_ten::decrease_by_ten(&test_config).await;
    }

    #[tokio::test]
    pub async fn decrease_by_ten_percent() {
        let test_config = InMemoryQueryConfig::new();
        decrease_by_ten::decrease_by_ten_percent(&test_config).await;
    }
}

mod exceeds_one_standard_deviation {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    pub async fn exceeds_one_standard_deviation() {
        let test_config = InMemoryQueryConfig::new();
        exceeds_one_standard_deviation::exceeds_one_standard_deviation(&test_config).await;
    }
}

mod unit_tests {
    use crate::sequence_counter;
    use drasi_core::in_memory_index::in_memory_checkpoint_store::InMemoryCheckpointStore;
    use drasi_core::in_memory_index::in_memory_result_index::InMemoryResultIndex;

    #[tokio::test]
    pub async fn sequence_counter() {
        let subject = InMemoryCheckpointStore::new();
        sequence_counter::sequence_counter(&subject).await;
    }

    #[tokio::test]
    pub async fn checkpoint_round_trip() {
        let subject = InMemoryCheckpointStore::new();
        sequence_counter::checkpoint_round_trip(&subject).await;
    }

    #[tokio::test]
    pub async fn result_sequence_counter() {
        let subject = InMemoryResultIndex::new();
        sequence_counter::result_sequence_counter(&subject).await;
    }
}

mod index {
    use crate::index;
    use drasi_core::{
        in_memory_index::in_memory_future_queue::InMemoryFutureQueue,
        interface::{FutureQueue, NoOpSessionControl},
    };
    use std::sync::Arc;

    #[tokio::test]
    async fn future_queue_push_always() {
        let fqi = InMemoryFutureQueue::new();
        let sc: Arc<dyn drasi_core::interface::SessionControl> = Arc::new(NoOpSessionControl);
        fqi.clear().await.unwrap();
        index::future_queue::push_always(&fqi, &sc).await;
    }

    #[tokio::test]
    async fn future_queue_push_not_exists() {
        let fqi = InMemoryFutureQueue::new();
        let sc: Arc<dyn drasi_core::interface::SessionControl> = Arc::new(NoOpSessionControl);
        fqi.clear().await.unwrap();
        index::future_queue::push_not_exists(&fqi, &sc).await;
    }

    #[tokio::test]
    async fn future_queue_clear_removes_all() {
        let fqi = InMemoryFutureQueue::new();
        let sc: Arc<dyn drasi_core::interface::SessionControl> = Arc::new(NoOpSessionControl);
        index::future_queue::clear_removes_all(&fqi, &sc).await;
    }

    #[tokio::test]
    async fn future_queue_push_overwrite() {
        let fqi = InMemoryFutureQueue::new();
        let sc: Arc<dyn drasi_core::interface::SessionControl> = Arc::new(NoOpSessionControl);
        fqi.clear().await.unwrap();
        index::future_queue::push_overwrite(&fqi, &sc).await;
    }
}

mod document {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn document() {
        let test_config = InMemoryQueryConfig::new();
        document::document(&test_config).await;
    }
}

mod unwind {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn unwind() {
        let test_config = InMemoryQueryConfig::new();
        unwind::unwind(&test_config).await;
    }

    #[tokio::test]
    async fn unwind_invalid_middleware_config() {
        let test_config = InMemoryQueryConfig::new();
        unwind::unwind_invalid_config_fails(&test_config).await;
    }

    #[tokio::test]
    async fn unwind_incorrect_middleware_structure() {
        let test_config = InMemoryQueryConfig::new();
        unwind::unwind_incorrect_structure_fails(&test_config).await;
    }
}

mod optional_match {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn optional_match() {
        let test_config = InMemoryQueryConfig::new();
        optional_match::optional_match(&test_config).await;
    }

    #[tokio::test]
    async fn optional_match_aggregating() {
        let test_config = InMemoryQueryConfig::new();
        optional_match::optional_match_aggregating(&test_config).await;
    }

    #[tokio::test]
    async fn multi_optional_match() {
        let test_config = InMemoryQueryConfig::new();
        optional_match::multi_optional_match(&test_config).await;
    }
}

mod decoder {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn decoder() {
        let test_config = InMemoryQueryConfig::new();
        decoder::decoder(&test_config).await;
    }
}

mod parse_json {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn parse_json_test() {
        let test_config = InMemoryQueryConfig::new();
        parse_json::parse_json_test(&test_config).await;
    }
}

mod promote {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn promote_test() {
        let test_config = InMemoryQueryConfig::new();
        promote::promote_test(&test_config).await;
    }
}

mod relabel {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn relabel() {
        let test_config = InMemoryQueryConfig::new();
        relabel::relabel(&test_config).await;
    }
}

mod dapr_state_store {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn dapr_state_store_test_suite() {
        let test_config = InMemoryQueryConfig::new();
        dapr_state_store::run_tests(&test_config).await;
    }
}

mod before {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn before_value() {
        let test_config = InMemoryQueryConfig::new();
        before::before_value(&test_config).await;
    }

    #[tokio::test]
    async fn before_sum() {
        let test_config = InMemoryQueryConfig::new();
        before::before_sum(&test_config).await;
    }
}

mod prev_unique {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn prev_unique() {
        let test_config = InMemoryQueryConfig::new();
        prev_distinct::prev_unique(&test_config).await;
    }

    #[tokio::test]
    async fn prev_unique_with_match() {
        let test_config = InMemoryQueryConfig::new();
        prev_distinct::prev_unique_with_match(&test_config).await;
    }
}

mod future_aggregations {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn truefor_sum() {
        let test_config = InMemoryQueryConfig::new();
        future_aggregations::truefor_sum(&test_config).await;
    }

    #[tokio::test]
    async fn truefor_grouped_sum() {
        let test_config = InMemoryQueryConfig::new();
        future_aggregations::truefor_grouped_sum(&test_config).await;
    }

    #[tokio::test]
    async fn truelater_max() {
        let test_config = InMemoryQueryConfig::new();
        future_aggregations::truelater_max(&test_config).await;
    }
}

mod windows {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn sliding_window_max() {
        let test_config = InMemoryQueryConfig::new();
        windows::sliding_window_max(&test_config).await;
    }

    #[tokio::test]
    async fn sliding_window_avg_grouped() {
        let test_config = InMemoryQueryConfig::new();
        windows::sliding_window_avg_grouped(&test_config).await;
    }
}

mod collect_aggregation {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn collect_based_aggregation_test() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::collect_based_aggregation_test(&test_config).await;
    }

    #[tokio::test]
    async fn simple_aggregation_test() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::simple_aggregation_test(&test_config).await;
    }

    #[tokio::test]
    async fn collect_with_filter() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::collect_with_filter_test(&test_config).await;
    }

    #[tokio::test]
    async fn collect_objects() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::collect_objects_test(&test_config).await;
    }

    #[tokio::test]
    async fn collect_mixed_types() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::collect_mixed_types_test(&test_config).await;
    }

    #[tokio::test]
    async fn multiple_collects() {
        let test_config = InMemoryQueryConfig::new();
        collect_aggregation::multiple_collects_test(&test_config).await;
    }
}

mod source_update_upsert {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn test_upsert_semantics() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_upsert_semantics(&test_config).await;
    }

    #[tokio::test]
    async fn test_partial_updates() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_partial_updates(&test_config).await;
    }

    #[tokio::test]
    async fn test_stateless_processing() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_stateless_processing(&test_config).await;
    }

    #[tokio::test]
    async fn test_query_matching() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_query_matching(&test_config).await;
    }

    #[tokio::test]
    async fn test_multiple_entities() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_multiple_entities(&test_config).await;
    }

    #[tokio::test]
    async fn test_relationship_upsert() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_relationship_upsert(&test_config).await;
    }

    #[tokio::test]
    async fn test_aggregation_with_upserts() {
        let test_config = InMemoryQueryConfig::new();
        source_update_upsert::test_aggregation_with_upserts(&test_config).await;
    }
}

mod string_operators {
    use super::InMemoryQueryConfig;
    use crate::use_cases::*;

    #[tokio::test]
    async fn starts_with() {
        let test_config = InMemoryQueryConfig::new();
        string_operators::starts_with(&test_config).await;
    }

    #[tokio::test]
    async fn ends_with() {
        let test_config = InMemoryQueryConfig::new();
        string_operators::ends_with(&test_config).await;
    }

    #[tokio::test]
    async fn contains_op() {
        let test_config = InMemoryQueryConfig::new();
        string_operators::contains_op(&test_config).await;
    }
}
