#pragma once

#include "foundation/result.h"

namespace pp::app {

enum class HistoryUiOperation {
    undo,
    redo,
    clear,
};

struct HistoryUiPlan {
    HistoryUiOperation operation = HistoryUiOperation::undo;
    int undo_count = 0;
    int redo_count = 0;
    int memory_bytes = 0;
    bool invokes_undo = false;
    bool invokes_redo = false;
    bool clears_history = false;
    bool updates_memory_label = false;
    bool updates_title = false;
    bool no_op = false;
};

class HistoryUiServices {
public:
    virtual ~HistoryUiServices() = default;

    virtual void invoke_undo() = 0;
    virtual void invoke_redo() = 0;
    virtual void clear_history() = 0;
};

[[nodiscard]] inline pp::foundation::Status validate_history_metric(int value, const char* message) noexcept
{
    if (value < 0) {
        return pp::foundation::Status::out_of_range(message);
    }

    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Result<HistoryUiPlan> plan_history_undo(int undo_count)
{
    const auto count_status = validate_history_metric(undo_count, "undo action count must not be negative");
    if (!count_status.ok()) {
        return pp::foundation::Result<HistoryUiPlan>::failure(count_status);
    }

    HistoryUiPlan plan;
    plan.operation = HistoryUiOperation::undo;
    plan.undo_count = undo_count;
    if (undo_count == 0) {
        plan.no_op = true;
        return pp::foundation::Result<HistoryUiPlan>::success(plan);
    }

    plan.invokes_undo = true;
    plan.updates_memory_label = true;
    plan.updates_title = true;
    return pp::foundation::Result<HistoryUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<HistoryUiPlan> plan_history_redo(int redo_count)
{
    const auto count_status = validate_history_metric(redo_count, "redo action count must not be negative");
    if (!count_status.ok()) {
        return pp::foundation::Result<HistoryUiPlan>::failure(count_status);
    }

    HistoryUiPlan plan;
    plan.operation = HistoryUiOperation::redo;
    plan.redo_count = redo_count;
    if (redo_count == 0) {
        plan.no_op = true;
        return pp::foundation::Result<HistoryUiPlan>::success(plan);
    }

    plan.invokes_redo = true;
    plan.updates_memory_label = true;
    plan.updates_title = true;
    return pp::foundation::Result<HistoryUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Result<HistoryUiPlan> plan_history_clear(
    int undo_count,
    int redo_count,
    int memory_bytes)
{
    const auto undo_status = validate_history_metric(undo_count, "undo action count must not be negative");
    if (!undo_status.ok()) {
        return pp::foundation::Result<HistoryUiPlan>::failure(undo_status);
    }
    const auto redo_status = validate_history_metric(redo_count, "redo action count must not be negative");
    if (!redo_status.ok()) {
        return pp::foundation::Result<HistoryUiPlan>::failure(redo_status);
    }
    const auto memory_status = validate_history_metric(memory_bytes, "history memory bytes must not be negative");
    if (!memory_status.ok()) {
        return pp::foundation::Result<HistoryUiPlan>::failure(memory_status);
    }

    HistoryUiPlan plan;
    plan.operation = HistoryUiOperation::clear;
    plan.undo_count = undo_count;
    plan.redo_count = redo_count;
    plan.memory_bytes = memory_bytes;
    if (undo_count == 0 && redo_count == 0 && memory_bytes == 0) {
        plan.no_op = true;
        return pp::foundation::Result<HistoryUiPlan>::success(plan);
    }

    plan.clears_history = true;
    plan.updates_memory_label = true;
    return pp::foundation::Result<HistoryUiPlan>::success(plan);
}

[[nodiscard]] inline pp::foundation::Status execute_history_ui_plan(
    const HistoryUiPlan& plan,
    HistoryUiServices& services)
{
    const auto undo_status = validate_history_metric(plan.undo_count, "undo action count must not be negative");
    if (!undo_status.ok()) {
        return undo_status;
    }
    const auto redo_status = validate_history_metric(plan.redo_count, "redo action count must not be negative");
    if (!redo_status.ok()) {
        return redo_status;
    }
    const auto memory_status = validate_history_metric(plan.memory_bytes, "history memory bytes must not be negative");
    if (!memory_status.ok()) {
        return memory_status;
    }

    if (plan.no_op) {
        return pp::foundation::Status::success();
    }

    switch (plan.operation) {
    case HistoryUiOperation::undo:
        if (plan.invokes_undo) {
            services.invoke_undo();
        }
        return pp::foundation::Status::success();
    case HistoryUiOperation::redo:
        if (plan.invokes_redo) {
            services.invoke_redo();
        }
        return pp::foundation::Status::success();
    case HistoryUiOperation::clear:
        if (plan.clears_history) {
            services.clear_history();
        }
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown history operation");
}

} // namespace pp::app
