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

namespace {

class FakeMainToolbarServices final : public pp::app::MainToolbarServices {
public:
    void show_open_dialog() override { open_dialogs += 1; }
    void show_save_dialog() override { save_dialogs += 1; }
    void invoke_undo(const pp::app::HistoryUiPlan& plan) override
    {
        undo_calls += 1;
        last_history = plan;
    }

    void invoke_redo(const pp::app::HistoryUiPlan& plan) override
    {
        redo_calls += 1;
        last_history = plan;
    }

    void clear_history(const pp::app::HistoryUiPlan& plan) override
    {
        clear_history_calls += 1;
        last_history = plan;
    }

    void clear_canvas(const pp::app::DocumentCanvasClearPlan& plan) override
    {
        clear_canvas_calls += 1;
        last_clear = plan;
    }
    void show_message_box() override { message_boxes += 1; }
    void show_settings_dialog() override { settings_dialogs += 1; }

    [[nodiscard]] int total_calls() const noexcept
    {
        return open_dialogs
            + save_dialogs
            + undo_calls
            + redo_calls
            + clear_history_calls
            + clear_canvas_calls
            + message_boxes
            + settings_dialogs;
    }

    int open_dialogs = 0;
    int save_dialogs = 0;
    int undo_calls = 0;
    int redo_calls = 0;
    int clear_history_calls = 0;
    int clear_canvas_calls = 0;
    int message_boxes = 0;
    int settings_dialogs = 0;
    pp::app::HistoryUiPlan last_history;
    pp::app::DocumentCanvasClearPlan last_clear;
};

void direct_dialog_commands_are_available(pp::tests::Harness& harness)
{
    const auto open = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::open_document);
    PP_EXPECT(harness, open);
    if (open) {
        PP_EXPECT(harness, open.value().action == pp::app::MainToolbarAction::show_open_dialog);
        PP_EXPECT(harness, open.value().label == "Open");
        PP_EXPECT(harness, !open.value().no_op);
    }

    const auto save = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::save_document);
    PP_EXPECT(harness, save);
    if (save) {
        PP_EXPECT(harness, save.value().action == pp::app::MainToolbarAction::show_save_dialog);
        PP_EXPECT(harness, save.value().label == "Save");
    }

    const auto settings = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::show_settings);
    PP_EXPECT(harness, settings);
    if (settings) {
        PP_EXPECT(harness, settings.value().action == pp::app::MainToolbarAction::show_settings_dialog);
        PP_EXPECT(harness, settings.value().label == "Settings");
    }
}

void message_box_dialog_preserves_legacy_defaults(pp::tests::Harness& harness)
{
    const auto plan = pp::app::plan_main_toolbar_message_dialog();

    PP_EXPECT(harness, plan.title == "Just a test message");
    PP_EXPECT(harness, plan.message == "Longer description for the error or the message.");
    PP_EXPECT(harness, plan.ok_caption == "Ok");
    PP_EXPECT(harness, plan.cancel_caption == "Cancel");
    PP_EXPECT(harness, plan.show_cancel);
}

void history_commands_reuse_history_breakpoints(pp::tests::Harness& harness)
{
    const auto undo = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::undo, 2);
    PP_EXPECT(harness, undo);
    if (undo) {
        PP_EXPECT(harness, undo.value().action == pp::app::MainToolbarAction::invoke_undo);
        PP_EXPECT(harness, undo.value().updates_memory_label);
        PP_EXPECT(harness, undo.value().updates_title);
        PP_EXPECT(harness, undo.value().history.operation == pp::app::HistoryUiOperation::undo);
        PP_EXPECT(harness, undo.value().history.invokes_undo);
        PP_EXPECT(harness, !undo.value().no_op);
    }

    const auto redo_empty = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::redo, 0, 0);
    PP_EXPECT(harness, redo_empty);
    if (redo_empty) {
        PP_EXPECT(harness, redo_empty.value().action == pp::app::MainToolbarAction::no_op_unavailable);
        PP_EXPECT(harness, redo_empty.value().label == "Redo (No history)");
        PP_EXPECT(harness, redo_empty.value().no_op);
    }

    const auto clear = pp::app::plan_main_toolbar_command(
        pp::app::MainToolbarCommand::clear_history,
        0,
        0,
        2048);
    PP_EXPECT(harness, clear);
    if (clear) {
        PP_EXPECT(harness, clear.value().action == pp::app::MainToolbarAction::clear_history);
        PP_EXPECT(harness, clear.value().updates_memory_label);
        PP_EXPECT(harness, !clear.value().updates_title);
        PP_EXPECT(harness, clear.value().history.operation == pp::app::HistoryUiOperation::clear);
        PP_EXPECT(harness, clear.value().history.clears_history);
    }
}

void canvas_clear_requires_live_canvas(pp::tests::Harness& harness)
{
    const auto clear = pp::app::plan_main_toolbar_command(
        pp::app::MainToolbarCommand::clear_canvas,
        0,
        0,
        0,
        true);
    PP_EXPECT(harness, clear);
    if (clear) {
        PP_EXPECT(harness, clear.value().action == pp::app::MainToolbarAction::clear_canvas);
        PP_EXPECT(harness, clear.value().requires_canvas);
        PP_EXPECT(harness, clear.value().records_undo);
        PP_EXPECT(harness, clear.value().marks_unsaved);
        PP_EXPECT(harness, clear.value().canvas_clear.clears_canvas);
        PP_EXPECT(harness, clear.value().canvas_clear.records_undo);
        PP_EXPECT(harness, clear.value().canvas_clear.marks_unsaved);
        PP_EXPECT(harness, !clear.value().no_op);
    }

    const auto missing_canvas = pp::app::plan_main_toolbar_command(
        pp::app::MainToolbarCommand::clear_canvas,
        0,
        0,
        0,
        false);
    PP_EXPECT(harness, missing_canvas);
    if (missing_canvas) {
        PP_EXPECT(harness, missing_canvas.value().action == pp::app::MainToolbarAction::no_op_unavailable);
        PP_EXPECT(harness, missing_canvas.value().label == "Clear Canvas (No canvas)");
        PP_EXPECT(harness, missing_canvas.value().requires_canvas);
        PP_EXPECT(harness, !missing_canvas.value().records_undo);
        PP_EXPECT(harness, !missing_canvas.value().marks_unsaved);
        PP_EXPECT(harness, !missing_canvas.value().canvas_clear.clears_canvas);
        PP_EXPECT(harness, missing_canvas.value().no_op);
    }
}

void rejects_negative_history_metrics(pp::tests::Harness& harness)
{
    PP_EXPECT(harness, !pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::undo, -1));
    PP_EXPECT(harness, !pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::redo, 0, -1));
    PP_EXPECT(harness, !pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::clear_history, 0, 0, -1));
}

void executor_dispatches_to_service_boundary(pp::tests::Harness& harness)
{
    FakeMainToolbarServices services;

    auto open = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::open_document);
    PP_EXPECT(harness, open);
    if (open) {
        const auto status = pp::app::execute_main_toolbar_plan(open.value(), services);
        PP_EXPECT(harness, status.ok());
        PP_EXPECT(harness, services.open_dialogs == 1);
    }

    auto undo = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::undo, 1);
    PP_EXPECT(harness, undo);
    if (undo) {
        const auto status = pp::app::execute_main_toolbar_plan(undo.value(), services);
        PP_EXPECT(harness, status.ok());
        PP_EXPECT(harness, services.undo_calls == 1);
        PP_EXPECT(harness, services.last_history.operation == pp::app::HistoryUiOperation::undo);
        PP_EXPECT(harness, services.last_history.undo_count == 1);
    }

    auto clear_canvas = pp::app::plan_main_toolbar_command(
        pp::app::MainToolbarCommand::clear_canvas,
        0,
        0,
        0,
        true);
    PP_EXPECT(harness, clear_canvas);
    if (clear_canvas) {
        const auto status = pp::app::execute_main_toolbar_plan(clear_canvas.value(), services);
        PP_EXPECT(harness, status.ok());
        PP_EXPECT(harness, services.clear_canvas_calls == 1);
        PP_EXPECT(harness, services.last_clear.clears_canvas);
        PP_EXPECT(harness, services.last_clear.records_undo);
        PP_EXPECT(harness, services.last_clear.marks_unsaved);
    }

    auto redo_empty = pp::app::plan_main_toolbar_command(pp::app::MainToolbarCommand::redo);
    PP_EXPECT(harness, redo_empty);
    if (redo_empty) {
        const int calls_before = services.total_calls();
        const auto status = pp::app::execute_main_toolbar_plan(redo_empty.value(), services);
        PP_EXPECT(harness, status.ok());
        PP_EXPECT(harness, services.total_calls() == calls_before);
    }
}

} // namespace

int main()
{
    pp::tests::Harness harness;
    harness.run("direct dialog commands are available", direct_dialog_commands_are_available);
    harness.run("message box dialog preserves legacy defaults", message_box_dialog_preserves_legacy_defaults);
    harness.run("history commands reuse history breakpoints", history_commands_reuse_history_breakpoints);
    harness.run("canvas clear requires live canvas", canvas_clear_requires_live_canvas);
    harness.run("rejects negative history metrics", rejects_negative_history_metrics);
    harness.run("executor dispatches to service boundary", executor_dispatches_to_service_boundary);
    return harness.finish();
}
