#include "app_core/about_menu.h"
#include "test_harness.h"

namespace {

class FakeAboutMenuServices final : public pp::app::AboutMenuServices {
public:
    void show_user_manual() override { manuals += 1; }
    void show_about_dialog() override { about_dialogs += 1; }
    void show_whats_new_dialog() override { whats_new_dialogs += 1; }
    void trigger_crash_test() override { crash_tests += 1; }
    void run_performance_test(const pp::app::AboutMenuPlan& plan) override
    {
        performance_tests += 1;
        performance_iterations = plan.performance_iterations;
        performance_updates_per_iteration = plan.performance_updates_per_iteration;
    }

    [[nodiscard]] int total_calls() const noexcept
    {
        return manuals + about_dialogs + whats_new_dialogs + crash_tests + performance_tests;
    }

    int manuals = 0;
    int about_dialogs = 0;
    int whats_new_dialogs = 0;
    int crash_tests = 0;
    int performance_tests = 0;
    int performance_iterations = 0;
    int performance_updates_per_iteration = 0;
};

void about_menu_maps_user_facing_dialog_actions(pp::tests::Harness& harness)
{
    const auto help = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::help_guide, 1, 2, 3);
    const auto about = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::about_app, 1, 2, 3);
    const auto news = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::whats_new, 1, 2, 3);

    PP_EXPECT(harness, help.action == pp::app::AboutMenuAction::show_user_manual);
    PP_EXPECT(harness, help.label == "Help Guide");
    PP_EXPECT(harness, help.closes_root_popup);
    PP_EXPECT(harness, about.action == pp::app::AboutMenuAction::show_about_dialog);
    PP_EXPECT(harness, about.label == "About PanoPainter");
    PP_EXPECT(harness, news.action == pp::app::AboutMenuAction::show_whats_new_dialog);
    PP_EXPECT(harness, news.label == "What's new in 1.2.3?");
}

void about_menu_gates_diagnostic_crash_action(pp::tests::Harness& harness)
{
    const auto enabled = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::induce_crash,
        1,
        2,
        3,
        true);
    const auto disabled = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::induce_crash,
        1,
        2,
        3,
        false);

    PP_EXPECT(harness, enabled.action == pp::app::AboutMenuAction::trigger_crash_test);
    PP_EXPECT(harness, enabled.closes_root_popup);
    PP_EXPECT(harness, disabled.action == pp::app::AboutMenuAction::no_op_unavailable);
    PP_EXPECT(harness, !disabled.closes_root_popup);
}

void about_menu_performance_test_declares_workload_and_canvas_requirement(pp::tests::Harness& harness)
{
    const auto available = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::performance_test,
        1,
        2,
        3,
        true,
        true);
    const auto no_canvas = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::performance_test,
        1,
        2,
        3,
        true,
        false);

    PP_EXPECT(harness, available.action == pp::app::AboutMenuAction::run_performance_test);
    PP_EXPECT(harness, available.requires_canvas);
    PP_EXPECT(harness, available.performance_iterations == 100);
    PP_EXPECT(harness, available.performance_updates_per_iteration == 10);
    PP_EXPECT(harness, available.closes_root_popup);
    PP_EXPECT(harness, no_canvas.action == pp::app::AboutMenuAction::no_op_unavailable);
    PP_EXPECT(harness, no_canvas.label == "Performance test (No canvas)");
    PP_EXPECT(harness, no_canvas.requires_canvas);
    PP_EXPECT(harness, !no_canvas.closes_root_popup);
}

void about_menu_executor_dispatches_to_services(pp::tests::Harness& harness)
{
    FakeAboutMenuServices services;

    const auto help = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::help_guide, 1, 2, 3);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(help, services).ok());
    PP_EXPECT(harness, services.manuals == 1);

    const auto about = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::about_app, 1, 2, 3);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(about, services).ok());
    PP_EXPECT(harness, services.about_dialogs == 1);

    const auto news = pp::app::plan_about_menu_command(pp::app::AboutMenuCommand::whats_new, 1, 2, 3);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(news, services).ok());
    PP_EXPECT(harness, services.whats_new_dialogs == 1);

    const auto crash = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::induce_crash,
        1,
        2,
        3,
        true);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(crash, services).ok());
    PP_EXPECT(harness, services.crash_tests == 1);

    const auto performance = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::performance_test,
        1,
        2,
        3,
        true,
        true);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(performance, services).ok());
    PP_EXPECT(harness, services.performance_tests == 1);
    PP_EXPECT(harness, services.performance_iterations == 100);
    PP_EXPECT(harness, services.performance_updates_per_iteration == 10);
}

void about_menu_executor_preserves_no_op_unavailable(pp::tests::Harness& harness)
{
    FakeAboutMenuServices services;

    const auto disabled_crash = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::induce_crash,
        1,
        2,
        3,
        false);
    PP_EXPECT(harness, disabled_crash.action == pp::app::AboutMenuAction::no_op_unavailable);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(disabled_crash, services).ok());
    PP_EXPECT(harness, services.total_calls() == 0);

    const auto no_canvas_perf = pp::app::plan_about_menu_command(
        pp::app::AboutMenuCommand::performance_test,
        1,
        2,
        3,
        true,
        false);
    PP_EXPECT(harness, no_canvas_perf.action == pp::app::AboutMenuAction::no_op_unavailable);
    PP_EXPECT(harness, pp::app::execute_about_menu_plan(no_canvas_perf, services).ok());
    PP_EXPECT(harness, services.total_calls() == 0);
}

}

int main()
{
    pp::tests::Harness harness;
    harness.run("about menu maps user facing dialog actions", about_menu_maps_user_facing_dialog_actions);
    harness.run("about menu gates diagnostic crash action", about_menu_gates_diagnostic_crash_action);
    harness.run(
        "about menu performance test declares workload and canvas requirement",
        about_menu_performance_test_declares_workload_and_canvas_requirement);
    harness.run("about menu executor dispatches to services", about_menu_executor_dispatches_to_services);
    harness.run("about menu executor preserves no op unavailable", about_menu_executor_preserves_no_op_unavailable);
    return harness.finish();
}
