#include "pch.h"

#include "legacy_document_session_services.h"

#include "action.h"
#include "app.h"
#include "legacy_app_dialog_services.h"
#include "legacy_document_canvas_services.h"
#include "legacy_canvas_view_services.h"
#include "legacy_history_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_dialog_open.h"

#include <utility>

namespace pp::panopainter {
namespace {

void log_legacy_document_save_snapshot(
    const char* context,
    const pp::app::DocumentCanvasSaveSnapshotReport& report)
{
    LOG(
        "%s document snapshot: %ux%u layers=%zu frames=%zu capturedFaces=%zu pendingFaces=%zu ppiReady=%s",
        context,
        report.width,
        report.height,
        report.layer_count,
        report.frame_count,
        report.captured_face_payloads,
        report.pending_face_payloads,
        report.can_export_ppi ? "true" : "false");
}

pp::foundation::Status prepare_legacy_document_save_snapshot(App& app, const char* context)
{
    auto snapshot = capture_legacy_canvas_document_payload_snapshot(app);
    if (!snapshot) {
        LOG("%s document snapshot failed: %s", context, snapshot.status().message);
        return snapshot.status();
    }

    const auto report = pp::app::make_document_canvas_save_snapshot_report(snapshot.value());
    log_legacy_document_save_snapshot(context, report);
    const auto route = pp::app::plan_document_canvas_save_writer_route(report);
    if (!route.uses_document_ppi_writer) {
        LOG(
            "%s document save writer retained legacy save: %.*s",
            context,
            static_cast<int>(route.fallback_reason.size()),
            route.fallback_reason.data());
        return pp::foundation::Status::success();
    }

    const auto exported = pp::app::export_document_canvas_save_snapshot_to_ppi(snapshot.value());
    if (!exported) {
        LOG(
            "%s document save writer retained legacy save after PPI export failure: %s",
            context,
            exported.status().message);
        return exported.status();
    }

    LOG(
        "%s document save writer route: document-ppi payloadComplete=%s bytes=%llu capturedFaces=%zu pendingFaces=%zu",
        context,
        exported.value().report.payload_complete ? "true" : "false",
        static_cast<unsigned long long>(exported.value().bytes.size()),
        exported.value().report.captured_face_payloads,
        exported.value().report.pending_face_payloads);
    return pp::foundation::Status::success();
}

void project_save_after_snapshot(App& app, const std::string& path)
{
    const auto status = prepare_legacy_document_save_snapshot(app, "project-save");
    if (!status.ok())
        LOG("project save snapshot bridge retained legacy save after failure: %s", status.message);
    app.canvas->m_canvas->project_save(path);
}

void project_save_after_snapshot(App& app)
{
    const auto status = prepare_legacy_document_save_snapshot(app, "project-save");
    if (!status.ok())
        LOG("project save snapshot bridge retained legacy save after failure: %s", status.message);
    app.canvas->m_canvas->project_save();
}

void project_save_after_snapshot(App& app, std::function<void(bool)> on_complete)
{
    const auto status = prepare_legacy_document_save_snapshot(app, "project-save-before-workflow");
    if (!status.ok())
        LOG("project save snapshot bridge retained legacy save after failure: %s", status.message);
    app.canvas->m_canvas->project_save(std::move(on_complete));
}

void wire_unsaved_close_prompt_buttons(
    App& app,
    bool& dialog_already_opened,
    const std::shared_ptr<NodeMessageBox>& dialog)
{
    dialog->btn_ok->on_click = [&app](Node*) {
        app.request_app_close();
        Canvas::I->m_unsaved = false;
    };
    dialog->btn_cancel->on_click = [&dialog_already_opened, dialog](Node*) {
        pp::panopainter::close_legacy_dialog_node(*dialog);
        dialog_already_opened = false;
    };
}

void create_legacy_new_document(
    App& app,
    const pp::app::NewDocumentPlan& plan,
    const std::shared_ptr<NodeDialogNewDoc>& dialog)
{
    app.doc_name = plan.target.name;
    app.doc_path = plan.target.path;
    app.doc_filename = plan.target.name + ".ppi";
    app.doc_dir = plan.target.directory;

    app.layers->clear();
    app.canvas->m_canvas->m_layers.clear();
    app.canvas->m_canvas->resize(plan.resolution, plan.resolution);
    const auto reset_status = execute_legacy_canvas_camera_reset(app);
    if (!reset_status.ok())
        LOG("New document camera reset failed: %s", reset_status.message);
    const auto history_status = pp::panopainter::execute_legacy_history_plan(
        pp::app::plan_new_document_history(plan));
    if (!history_status.ok()) {
        LOG("New document history effect failed: %s", history_status.message);
    }

    app.layers->add_layer("Default", false, true);

    app.canvas->m_canvas->m_unsaved = true;
    app.canvas->m_canvas->m_newdoc = false;
    app.title_update();

    pp::panopainter::close_legacy_dialog_and_hide_keyboard(app, *dialog);
}

template <typename Dialog, typename Plan, typename AcceptAction>
void wire_legacy_overwrite_prompt_ok(
    App& app,
    const std::shared_ptr<NodeMessageBox>& msgbox,
    const std::shared_ptr<Dialog>& dialog,
    const Plan& plan,
    AcceptAction&& accept_action)
{
    auto* app_ptr = &app;
    auto msgbox_ptr = msgbox;
    auto dialog_ptr = dialog;
    msgbox->btn_ok->on_click = [app_ptr, msgbox_ptr, dialog_ptr, plan, accept_action = std::forward<AcceptAction>(accept_action)](Node*) {
        accept_action(*app_ptr, plan, dialog_ptr);
        pp::panopainter::close_legacy_dialog_node(*msgbox_ptr);
    };
}

class LegacyNewDocumentServices final : public pp::app::NewDocumentServices {
public:
    LegacyNewDocumentServices(App& app, std::shared_ptr<NodeDialogNewDoc> dialog) noexcept
        : app_(app)
        , dialog_(std::move(dialog))
    {
    }

    void create_new_document(const pp::app::NewDocumentPlan& plan) override
    {
        create_legacy_new_document(app_, plan, dialog_);
    }

    void prompt_overwrite_new_document(const pp::app::NewDocumentPlan& plan) override
    {
        auto msgbox = pp::panopainter::create_legacy_app_message_dialog(
            app_,
            pp::app::plan_document_session_prompt(
                pp::app::DocumentSessionPromptKind::new_document_overwrite));
        wire_legacy_overwrite_prompt_ok(app_, msgbox, dialog_, plan, create_legacy_new_document);
    }

private:
    App& app_;
    std::shared_ptr<NodeDialogNewDoc> dialog_;
};

void save_legacy_document_file(
    App& app,
    const pp::app::DocumentFileSavePlan& plan,
    const std::shared_ptr<NodeDialogSave>& dialog)
{
    const auto history_status = pp::panopainter::execute_legacy_history_plan(
        pp::app::plan_document_file_save_history(plan));
    if (!history_status.ok()) {
        LOG("Document file save history effect failed: %s", history_status.message);
    }
    project_save_after_snapshot(app, plan.target.path);
    app.doc_name = plan.target.name;
    app.doc_path = plan.target.path;
    app.doc_dir = plan.target.directory;
    app.title_update();
    pp::panopainter::close_legacy_dialog_and_hide_keyboard(app, *dialog);
}

class LegacyDocumentFileSaveServices final : public pp::app::DocumentFileSaveServices {
public:
    LegacyDocumentFileSaveServices(App& app, std::shared_ptr<NodeDialogSave> dialog) noexcept
        : app_(app)
        , dialog_(std::move(dialog))
    {
    }

    void save_document_file(const pp::app::DocumentFileSavePlan& plan) override
    {
        save_legacy_document_file(app_, plan, dialog_);
    }

    void prompt_overwrite_document_file(const pp::app::DocumentFileSavePlan& plan) override
    {
        auto msgbox = pp::panopainter::create_legacy_app_message_dialog(
            app_,
            pp::app::plan_document_session_prompt(
                pp::app::DocumentSessionPromptKind::document_file_overwrite,
                plan.target.name));
        wire_legacy_overwrite_prompt_ok(app_, msgbox, dialog_, plan, save_legacy_document_file);
    }

private:
    App& app_;
    std::shared_ptr<NodeDialogSave> dialog_;
};

void save_legacy_document_version(App& app, const pp::app::DocumentVersionTarget& target);

class LegacyDocumentVersionSaveServices final : public pp::app::DocumentVersionSaveServices {
public:
    explicit LegacyDocumentVersionSaveServices(App& app) noexcept
        : app_(app)
    {
    }

    void save_document_version(const pp::app::DocumentVersionTarget& target) override
    {
        save_legacy_document_version(app_, target);
    }

private:
    App& app_;
};

void save_legacy_document_version(App& app, const pp::app::DocumentVersionTarget& target)
{
    const auto history_status = pp::panopainter::execute_legacy_history_plan(
        pp::app::plan_document_version_save_history(target));
    if (!history_status.ok()) {
        LOG("Document version history effect failed: %s", history_status.message);
    }
    app.doc_name = target.name;
    app.doc_path = target.path;
    app.canvas->m_canvas->m_unsaved = true;
    app.title_update();
    project_save_after_snapshot(app, app.doc_path);
}

class LegacyCloseRequestServices final : public pp::app::CloseRequestServices {
public:
    LegacyCloseRequestServices(App& app, bool& dialog_already_opened) noexcept
        : app_(app)
        , dialog_already_opened_(dialog_already_opened)
    {
    }

    void request_close_now() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_close_request_history(pp::app::CloseRequestDecision::close_now));
        if (!history_status.ok()) {
            LOG("Close request history effect failed: %s", history_status.message);
        }
    }

    void show_unsaved_close_prompt() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_close_request_history(pp::app::CloseRequestDecision::show_unsaved_prompt));
        if (!history_status.ok()) {
            LOG("Close prompt history effect failed: %s", history_status.message);
        }
        auto m = pp::panopainter::create_legacy_app_message_dialog(
            app_,
            pp::app::plan_document_session_prompt(
                pp::app::DocumentSessionPromptKind::close_unsaved_document));
        wire_unsaved_close_prompt_buttons(app_, dialog_already_opened_, m);
        dialog_already_opened_ = true;
    }

private:
    App& app_;
    bool& dialog_already_opened_;
};

class LegacyDocumentSaveServices final : public pp::app::DocumentSaveServices {
public:
    explicit LegacyDocumentSaveServices(App& app) noexcept
        : app_(app)
    {
    }

    void show_save_dialog() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_document_save_history(pp::app::DocumentSaveDecision::show_save_dialog));
        if (!history_status.ok()) {
            LOG("Save dialog history effect failed: %s", history_status.message);
        }
        app_.dialog_save();
    }

    void save_existing_document() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_document_save_history(pp::app::DocumentSaveDecision::save_existing));
        if (!history_status.ok()) {
            LOG("Save existing history effect failed: %s", history_status.message);
        }
        project_save_after_snapshot(app_);
    }

    void save_document_version() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_document_save_history(pp::app::DocumentSaveDecision::save_version));
        if (!history_status.ok()) {
            LOG("Save version history effect failed: %s", history_status.message);
        }
        app_.dialog_save_ver();
    }

private:
    App& app_;
};

void wire_prompt_save_before_continue_buttons(
    App& app,
    const std::shared_ptr<NodeMessageBox>& dialog,
    std::function<void()> action)
{
    auto* app_ptr = &app;
    dialog->btn_ok->on_click = [app_ptr, dialog, action](Node*) {
        project_save_after_snapshot(*app_ptr, [app_ptr, dialog, action](bool success) {
            if (success)
                action();
            else {
                const auto plan = pp::app::plan_document_session_prompt(
                    pp::app::DocumentSessionPromptKind::document_save_error);
                app_ptr->message_box(plan.title, plan.message, plan.show_cancel);
            }
        });
        pp::panopainter::close_legacy_dialog_node(*dialog);
    };
    dialog->btn_cancel->on_click = [dialog, action](Node*) {
        action();
        pp::panopainter::close_legacy_dialog_node(*dialog);
    };
}

class LegacyDocumentWorkflowServices final : public pp::app::DocumentWorkflowServices {
public:
    LegacyDocumentWorkflowServices(App& app, std::function<void()> action) noexcept
        : app_(app)
        , action_(std::move(action))
    {
    }

    void continue_workflow_now() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_document_workflow_history(
                pp::app::DocumentWorkflowDecision::continue_now));
        if (!history_status.ok()) {
            LOG("Workflow continue history effect failed: %s", history_status.message);
        }
        action_();
    }

    void prompt_save_before_continue() override
    {
        const auto history_status = pp::panopainter::execute_legacy_history_plan(
            pp::app::plan_document_workflow_history(
                pp::app::DocumentWorkflowDecision::prompt_save_before_continue));
        if (!history_status.ok()) {
            LOG("Workflow prompt history effect failed: %s", history_status.message);
        }
        auto m = pp::panopainter::create_legacy_app_message_dialog(
            app_,
            pp::app::plan_document_session_prompt(
                pp::app::DocumentSessionPromptKind::save_before_workflow_continue));
        wire_prompt_save_before_continue_buttons(app_, m, action_);
    }

private:
    App& app_;
    std::function<void()> action_;
};

} // namespace

pp::foundation::Status execute_legacy_close_request_decision(
    App& app,
    pp::app::CloseRequestDecision decision,
    bool& dialog_already_opened)
{
    LegacyCloseRequestServices services(app, dialog_already_opened);
    return pp::app::execute_close_request_decision(decision, services);
}

pp::foundation::Status execute_legacy_document_save_decision(
    App& app,
    pp::app::DocumentSaveDecision decision)
{
    LegacyDocumentSaveServices services(app);
    return pp::app::execute_document_save_decision(decision, services);
}

pp::foundation::Status execute_legacy_document_workflow_decision(
    App& app,
    pp::app::DocumentWorkflowDecision decision,
    std::function<void()> action)
{
    LegacyDocumentWorkflowServices services(app, std::move(action));
    return pp::app::execute_document_workflow_decision(decision, services);
}

pp::foundation::Status execute_legacy_new_document_plan(
    App& app,
    const pp::app::NewDocumentPlan& plan,
    std::shared_ptr<NodeDialogNewDoc> dialog)
{
    LegacyNewDocumentServices services(app, std::move(dialog));
    return pp::app::execute_new_document_plan(plan, services);
}

pp::foundation::Status execute_legacy_document_file_save_plan(
    App& app,
    const pp::app::DocumentFileSavePlan& plan,
    std::shared_ptr<NodeDialogSave> dialog)
{
    LegacyDocumentFileSaveServices services(app, std::move(dialog));
    return pp::app::execute_document_file_save_plan(plan, services);
}

pp::foundation::Status execute_legacy_document_version_save(
    App& app,
    const pp::app::DocumentVersionTarget& target)
{
    LegacyDocumentVersionSaveServices services(app);
    return pp::app::execute_document_version_save(target, services);
}

void execute_legacy_document_save_before_cloud_upload(App& app)
{
    Canvas::I->project_save_thread(app.doc_path, true);
}

} // namespace pp::panopainter
