#pragma once

#include "foundation/result.h"

#include <cstddef>
#include <limits>
#include <string>
#include <string_view>

namespace pp::app {

enum class CloudUploadAction {
    unavailable_no_canvas,
    show_save_required_warning,
    prompt_publish,
};

enum class CloudBrowseAction {
    unavailable_no_canvas,
    show_browser,
};

enum class CloudDownloadSelectionAction {
    wait_for_selection,
    start_download,
};

struct CloudUploadPlan {
    CloudUploadAction action = CloudUploadAction::unavailable_no_canvas;
    bool save_before_upload = false;
};

struct CloudBulkUploadPlan {
    std::size_t file_count = 0;
    int progress_total = 0;
    bool show_progress = false;
};

struct CloudDownloadRequest {
    std::string selected_file;
    std::string selected_path;
    std::string selected_name;
};

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

    virtual void show_save_required_warning() = 0;
    virtual void prompt_publish(bool save_before_upload) = 0;
    virtual void begin_bulk_upload(int progress_total, bool show_progress) = 0;
    virtual void upload_all_bulk_files() = 0;
    virtual void end_bulk_upload() = 0;
    virtual void show_browser() = 0;
    virtual void start_download(const CloudDownloadRequest& request) = 0;
};

[[nodiscard]] constexpr CloudUploadPlan plan_cloud_upload(
    bool has_canvas,
    bool is_new_document,
    bool has_unsaved_changes) noexcept
{
    if (!has_canvas) {
        return { CloudUploadAction::unavailable_no_canvas, false };
    }

    if (is_new_document) {
        return { CloudUploadAction::show_save_required_warning, false };
    }

    return { CloudUploadAction::prompt_publish, has_unsaved_changes };
}

[[nodiscard]] constexpr CloudBrowseAction plan_cloud_browse(bool has_canvas) noexcept
{
    return has_canvas
        ? CloudBrowseAction::show_browser
        : CloudBrowseAction::unavailable_no_canvas;
}

[[nodiscard]] constexpr CloudDownloadSelectionAction plan_cloud_download_selection(
    std::string_view selected_file) noexcept
{
    return selected_file.empty()
        ? CloudDownloadSelectionAction::wait_for_selection
        : CloudDownloadSelectionAction::start_download;
}

[[nodiscard]] constexpr CloudBulkUploadPlan plan_cloud_bulk_upload(
    std::size_t file_count,
    bool progress_ui_available) noexcept
{
    const auto max_progress_total = static_cast<std::size_t>(std::numeric_limits<int>::max());
    return {
        file_count,
        file_count > max_progress_total ? std::numeric_limits<int>::max() : static_cast<int>(file_count),
        progress_ui_available,
    };
}

[[nodiscard]] inline pp::foundation::Status execute_cloud_upload_plan(
    const CloudUploadPlan& plan,
    CloudServices& services)
{
    switch (plan.action) {
    case CloudUploadAction::unavailable_no_canvas:
        return pp::foundation::Status::success();
    case CloudUploadAction::show_save_required_warning:
        services.show_save_required_warning();
        return pp::foundation::Status::success();
    case CloudUploadAction::prompt_publish:
        services.prompt_publish(plan.save_before_upload);
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown cloud upload action");
}

[[nodiscard]] inline pp::foundation::Status execute_cloud_bulk_upload_plan(
    const CloudBulkUploadPlan& plan,
    CloudServices& services)
{
    services.begin_bulk_upload(plan.progress_total, plan.show_progress);
    services.upload_all_bulk_files();
    services.end_bulk_upload();
    return pp::foundation::Status::success();
}

[[nodiscard]] inline pp::foundation::Status execute_cloud_browse_action(
    CloudBrowseAction action,
    CloudServices& services)
{
    switch (action) {
    case CloudBrowseAction::unavailable_no_canvas:
        return pp::foundation::Status::success();
    case CloudBrowseAction::show_browser:
        services.show_browser();
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown cloud browse action");
}

[[nodiscard]] inline pp::foundation::Status execute_cloud_download_selection_action(
    CloudDownloadSelectionAction action,
    CloudServices& services,
    const CloudDownloadRequest& request)
{
    switch (action) {
    case CloudDownloadSelectionAction::wait_for_selection:
        return pp::foundation::Status::success();
    case CloudDownloadSelectionAction::start_download:
        if (request.selected_file.empty()) {
            return pp::foundation::Status::invalid_argument("cloud download requires a selected file");
        }
        services.start_download(request);
        return pp::foundation::Status::success();
    }

    return pp::foundation::Status::invalid_argument("unknown cloud download selection action");
}

}
