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

namespace {

class FakeToolsMenuServices final : public pp::app::ToolsMenuServices {
public:
    void show_panels_submenu() override { panels_submenus += 1; }
    void show_options_submenu() override { options_submenus += 1; }
    void clear_grid_overlays() override { clear_grid_calls += 1; }
    void reset_camera() override { reset_camera_calls += 1; }
    void show_shortcuts_dialog() override { shortcut_dialogs += 1; }
    void start_sonarpen() override { sonarpen_starts += 1; }

    [[nodiscard]] int total_calls() const noexcept
    {
        return panels_submenus
            + options_submenus
            + clear_grid_calls
            + reset_camera_calls
            + shortcut_dialogs
            + sonarpen_starts;
    }

    int panels_submenus = 0;
    int options_submenus = 0;
    int clear_grid_calls = 0;
    int reset_camera_calls = 0;
    int shortcut_dialogs = 0;
    int sonarpen_starts = 0;
};

void tools_menu_maps_submenus_and_commands(pp::tests::Harness& harness)
{
    const auto panels = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels);
    const auto options = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::options);
    const auto clear = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids);
    const auto camera = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera);
    const auto shortcuts = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts);

    PP_EXPECT(harness, panels.action == pp::app::ToolsMenuAction::show_panels_submenu);
    PP_EXPECT(harness, !panels.closes_root_popup);
    PP_EXPECT(harness, options.action == pp::app::ToolsMenuAction::show_options_submenu);
    PP_EXPECT(harness, clear.action == pp::app::ToolsMenuAction::clear_grid_overlays);
    PP_EXPECT(harness, clear.closes_root_popup);
    PP_EXPECT(harness, camera.action == pp::app::ToolsMenuAction::reset_camera);
    PP_EXPECT(harness, camera.closes_root_popup);
    PP_EXPECT(harness, shortcuts.action == pp::app::ToolsMenuAction::show_shortcuts_dialog);
    PP_EXPECT(harness, shortcuts.closes_root_popup);
}

void tools_menu_gates_platform_only_actions(pp::tests::Harness& harness)
{
    const auto unavailable = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::sonarpen);
    const auto available = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::sonarpen, true);

    PP_EXPECT(harness, unavailable.action == pp::app::ToolsMenuAction::no_op_unavailable);
    PP_EXPECT(harness, !unavailable.closes_root_popup);
    PP_EXPECT(harness, available.action == pp::app::ToolsMenuAction::start_sonarpen);
    PP_EXPECT(harness, available.closes_root_popup);
}

void tools_panel_plans_floating_panel_metadata(pp::tests::Harness& harness)
{
    const auto presets = pp::app::plan_tools_panel(pp::app::ToolsPanel::presets, false);
    const auto color = pp::app::plan_tools_panel(pp::app::ToolsPanel::color, false);
    const auto layers = pp::app::plan_tools_panel(pp::app::ToolsPanel::layers, false);
    const auto animation = pp::app::plan_tools_panel(pp::app::ToolsPanel::animation, false);

    PP_EXPECT(harness, presets.action == pp::app::ToolsPanelAction::open_floating_panel);
    PP_EXPECT(harness, presets.title == "Brushes");
    PP_EXPECT(harness, presets.height == 300);
    PP_EXPECT(harness, presets.min_height == 300);
    PP_EXPECT(harness, presets.min_width == 100);
    PP_EXPECT(harness, color.title == "Color Picker");
    PP_EXPECT(harness, color.hides_embedded_title);
    PP_EXPECT(harness, layers.title == "Layers");
    PP_EXPECT(harness, layers.min_height == 100);
    PP_EXPECT(harness, layers.hides_embedded_title);
    PP_EXPECT(harness, animation.title == "Animation");
    PP_EXPECT(harness, animation.width == 500);
    PP_EXPECT(harness, animation.height == 300);
    PP_EXPECT(harness, !animation.droppable);
}

void tools_panel_no_ops_when_panel_is_already_visible(pp::tests::Harness& harness)
{
    const auto visible = pp::app::plan_tools_panel(pp::app::ToolsPanel::brush, true);
    const auto hidden = pp::app::plan_tools_panel(pp::app::ToolsPanel::brush, false);

    PP_EXPECT(harness, visible.action == pp::app::ToolsPanelAction::no_op_already_visible);
    PP_EXPECT(harness, hidden.action == pp::app::ToolsPanelAction::open_floating_panel);
    PP_EXPECT(harness, visible.title == hidden.title);
    PP_EXPECT(harness, visible.hides_embedded_title == hidden.hides_embedded_title);
}

void tools_menu_executor_dispatches_direct_commands(pp::tests::Harness& harness)
{
    FakeToolsMenuServices services;

    const auto panels = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::panels);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(panels, services).ok());
    PP_EXPECT(harness, services.panels_submenus == 1);

    const auto options = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::options);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(options, services).ok());
    PP_EXPECT(harness, services.options_submenus == 1);

    const auto clear = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::clear_grids);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(clear, services).ok());
    PP_EXPECT(harness, services.clear_grid_calls == 1);

    const auto camera = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::reset_camera);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(camera, services).ok());
    PP_EXPECT(harness, services.reset_camera_calls == 1);

    const auto shortcuts = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::shortcuts);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(shortcuts, services).ok());
    PP_EXPECT(harness, services.shortcut_dialogs == 1);

    const auto sonarpen = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::sonarpen, true);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(sonarpen, services).ok());
    PP_EXPECT(harness, services.sonarpen_starts == 1);
}

void tools_menu_executor_preserves_unavailable_no_op(pp::tests::Harness& harness)
{
    FakeToolsMenuServices services;

    const auto unavailable = pp::app::plan_tools_menu_command(pp::app::ToolsMenuCommand::sonarpen, false);
    PP_EXPECT(harness, unavailable.action == pp::app::ToolsMenuAction::no_op_unavailable);
    PP_EXPECT(harness, pp::app::execute_tools_menu_plan(unavailable, services).ok());
    PP_EXPECT(harness, services.total_calls() == 0);
}

}

int main()
{
    pp::tests::Harness harness;
    harness.run("tools menu maps submenus and commands", tools_menu_maps_submenus_and_commands);
    harness.run("tools menu gates platform only actions", tools_menu_gates_platform_only_actions);
    harness.run("tools panel plans floating panel metadata", tools_panel_plans_floating_panel_metadata);
    harness.run("tools panel no ops when panel is already visible", tools_panel_no_ops_when_panel_is_already_visible);
    harness.run("tools menu executor dispatches direct commands", tools_menu_executor_dispatches_direct_commands);
    harness.run("tools menu executor preserves unavailable no op", tools_menu_executor_preserves_unavailable_no_op);
    return harness.finish();
}
