#include <gtest/gtest.h>
#include <limits>
#include <stdexcept>
#include "Low_Latency_Strategy_Test_Types.hpp"
TEST(low_latency_strategy_test, publishes_and_renders_latest_frame) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    {
        auto frame = strategy.acquire_painter();
        frame->value = 7;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
        EXPECT_EQ(frame->value, 7);
    }
    EXPECT_EQ(strategy.state().completed_lifecycle_count, 1);
}
TEST(low_latency_strategy_test, abandons_unconsumed_cached_frame) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    {
        auto frame = strategy.acquire_painter();
        frame->value = 1;
    }
    {
        auto frame = strategy.acquire_painter();
        frame->value = 2;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
        EXPECT_EQ(frame->value, 2);
    }
    EXPECT_EQ(strategy.counter_statistics().abandoned_frame_count, 1);
}
TEST(low_latency_strategy_test, manually_discards_stale_latest_data_frame) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.on_real_time_data_update({Real_Time_Data_Observation_Event::updated, {nullptr, Real_Time_Data_Retention::latest, 1, 10, 1, 1}});
    {
        auto frame = strategy.acquire_painter();
        frame->value = 1;
    }
    strategy.on_real_time_data_update({Real_Time_Data_Observation_Event::updated, {nullptr, Real_Time_Data_Retention::latest, 2, 20, 2, 1}});
    EXPECT_TRUE(strategy.discard_stale_latest_data_frame());
    EXPECT_EQ(strategy.counter_statistics().manually_discarded_frame_count, 1);
    auto frame = strategy.acquire_renderer();
    EXPECT_FALSE(frame);
}
TEST(low_latency_strategy_test, does_not_discard_current_data_frame) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.on_real_time_data_update({Real_Time_Data_Observation_Event::updated, {nullptr, Real_Time_Data_Retention::latest, 1, 10, 1, 1}});
    {
        auto frame = strategy.acquire_painter();
        frame->value = 9;
    }
    EXPECT_FALSE(strategy.discard_stale_latest_data_frame());
    auto frame = strategy.acquire_renderer();
    ASSERT_TRUE(frame);
    EXPECT_EQ(frame->value, 9);
}
TEST(low_latency_strategy_test, updates_frequency_after_completed_lifecycle) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(100.0);
    EXPECT_DOUBLE_EQ(strategy.state().frequency_hz, 100.0);
    {
        auto frame = strategy.acquire_painter();
        frame->value = 3;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
    }
    EXPECT_DOUBLE_EQ(strategy.state().frequency_hz, 100.0);
    EXPECT_DOUBLE_EQ(strategy.frequency_hz(), 60.0);
    strategy.swap();
    EXPECT_DOUBLE_EQ(strategy.frequency_hz(), 100.0);
    EXPECT_EQ(strategy.next_refresh_interval_ns(), 10'000'000);
}
TEST(low_latency_strategy_test, detects_render_limited_state) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(1'000'000.0);
    {
        auto frame = strategy.acquire_painter();
        frame->value = 5;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
        time_source.advance_ns(5'000);
    }
    EXPECT_EQ(strategy.state().limit_state, Low_Latency_Test_Strategy::Limit_State::render_limited);
}
TEST(low_latency_strategy_test, rejects_non_finite_frequency_configuration) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    EXPECT_THROW((Low_Latency_Test_Strategy(Low_Latency_Test_Observer_State(observer, time_source), {std::numeric_limits<double>::quiet_NaN()})), std::invalid_argument);
    EXPECT_THROW((Low_Latency_Test_Strategy(Low_Latency_Test_Observer_State(observer, time_source), {std::numeric_limits<double>::infinity()})), std::invalid_argument);
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    EXPECT_THROW(strategy.set_frequency_hz(std::numeric_limits<double>::quiet_NaN()), std::invalid_argument);
    EXPECT_THROW(strategy.set_frequency_hz(std::numeric_limits<double>::infinity()), std::invalid_argument);
}
TEST(low_latency_strategy_test, clamps_extremely_small_frequency_interval_without_overflow) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(1e-300);
    {
        auto frame = strategy.acquire_painter();
        frame->value = 1;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
    }
    EXPECT_EQ(strategy.state().target_interval_ns, std::numeric_limits<std::uint64_t>::max());
}
TEST(low_latency_strategy_test, separates_consumer_rate_from_jitter_safety_margin) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(1000.0);
    strategy.set_consumer_feedback({10'000'000});
    auto state = strategy.state();
    EXPECT_TRUE(state.consumer_feedback_enabled);
    EXPECT_EQ(state.consumer_sample_interval_ns, 10'000'000u);
    EXPECT_EQ(state.consumer_smoothed_interval_ns, 10'000'000u);
    EXPECT_EQ(state.consumer_variation_ns, 0u);
    EXPECT_EQ(state.consumer_safety_interval_ns, 10'000'000u);
    EXPECT_EQ(state.consumer_interval_ns, 10'000'000u);
    strategy.set_consumer_feedback({14'000'000});
    state = strategy.state();
    EXPECT_EQ(state.consumer_sample_interval_ns, 14'000'000u);
    EXPECT_EQ(state.consumer_smoothed_interval_ns, 12'000'000u);
    EXPECT_EQ(state.consumer_variation_ns, 250'000u);
    EXPECT_EQ(state.consumer_safety_interval_ns, 14'000'000u);
    EXPECT_EQ(state.consumer_interval_ns, 14'000'000u);
    EXPECT_EQ(state.next_refresh_interval_ns, 14'000'000u);
    EXPECT_EQ(state.limit_state, Low_Latency_Test_Strategy::Limit_State::consumer_limited);
}
TEST(low_latency_strategy_test, consumer_jitter_changes_safety_deadline_without_becoming_rate_limit) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(1000.0);
    strategy.set_consumer_feedback({20'000'000});
    strategy.set_consumer_feedback({22'000'000});
    strategy.set_consumer_feedback({20'000'000});
    const auto state = strategy.state();
    EXPECT_EQ(state.consumer_sample_interval_ns, 20'000'000u);
    EXPECT_EQ(state.consumer_smoothed_interval_ns, 20'875'000u);
    EXPECT_EQ(state.consumer_variation_ns, 242'187u);
    EXPECT_EQ(state.consumer_interval_ns, 20'875'000u);
    EXPECT_EQ(state.consumer_safety_interval_ns, 21'843'748u);
    EXPECT_EQ(state.next_refresh_interval_ns, 20'875'000u);
}
TEST(low_latency_strategy_test, allows_frequency_and_consumer_limits_to_be_cleared) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.set_frequency_hz(1000.0);
    strategy.set_consumer_feedback({20'000'000});
    strategy.clear_consumer_feedback();
    auto state = strategy.state();
    EXPECT_FALSE(state.consumer_feedback_enabled);
    EXPECT_EQ(state.consumer_safety_interval_ns, 0u);
    EXPECT_EQ(state.consumer_interval_ns, 0u);
    EXPECT_EQ(state.limit_state, Low_Latency_Test_Strategy::Limit_State::frequency_limited);
    strategy.clear_frequency_limit();
    state = strategy.state();
    EXPECT_FALSE(state.frequency_limit_enabled);
    EXPECT_TRUE(std::isnan(state.frequency_hz));
    EXPECT_EQ(state.target_interval_ns, 0u);
    EXPECT_EQ(state.next_refresh_interval_ns, 0u);
    EXPECT_EQ(state.limit_state, Low_Latency_Test_Strategy::Limit_State::unlimited);
}
TEST(low_latency_strategy_test, unlimited_mode_runs_at_measured_render_bottleneck) {
    Low_Latency_Test_Time_Source time_source;
    Low_Latency_Test_Observer observer;
    auto strategy = make_low_latency_test_strategy(time_source, observer);
    strategy.clear_frequency_limit();
    strategy.clear_consumer_feedback();
    {
        auto frame = strategy.acquire_painter();
        time_source.advance_ns(2'000);
        frame->value = 1;
    }
    {
        auto frame = strategy.acquire_renderer();
        ASSERT_TRUE(frame);
        time_source.advance_ns(5'000);
    }
    const auto state = strategy.state();
    EXPECT_EQ(state.target_interval_ns, 0u);
    EXPECT_EQ(state.consumer_interval_ns, 0u);
    EXPECT_EQ(state.bottleneck_duration_ns, 5'000u);
    EXPECT_EQ(state.next_refresh_interval_ns, 5'000u);
    EXPECT_EQ(state.limit_state, Low_Latency_Test_Strategy::Limit_State::render_limited);
}
