// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "publish.h"

#include <exception>

#define ADDR ", it should look like this \"node@service.provicer.zone\""
#define CANCEL "send \"cancel\" to end the dialog"

Module::Publish::Publish(const std::shared_ptr<Core>& core, const Config::Module& conf):
    Module(core, conf, "Publish")
{}

Module::Publish::~Publish() noexcept {}

Shared::Result Module::Publish::message(const std::shared_ptr<::Actor>& actor, std::string_view view) {
    Shared::Result result = handleDialog(actor, view);
    if (result != Shared::unhandled)
        return result;

    std::string_view command = pop(view);

    if (command == "to") {
        if (!hasPermission("publish", actor))
            return Shared::forbidden;
        
        std::string_view head = pop(view, '\n');
        std::string_view address = pop(head);
        if (address.empty())
            return Shared::error;

        return to(address, head, view);
    } else if (command == "dialog") {
        if (!hasPermission("publish", actor))
            return Shared::forbidden;
        
        dialogs.emplace(actor->jid, Dialog::Stage::address);
        core->send(actor->jid, "Please provide an address where the publication should take place" ADDR);

        return Shared::grab;
    }

    return Shared::unhandled;
}

void Module::Publish::cancelDialog(const std::shared_ptr<::Actor>& actor) {
    dialogs.erase(actor->jid);
}

Shared::Result Module::Publish::handleDialog(const std::shared_ptr<::Actor>& actor, std::string_view message) {
    std::map<std::string, Dialog>::iterator itr = dialogs.find(actor->jid);
    if (itr == dialogs.end())
        return Shared::unhandled;

    Dialog& dialog = itr->second;
    switch (dialog.stage) {
        case Dialog::Stage::address:
            return dialogAddress(itr->first, dialog, message);
        case Dialog::Stage::title:
            return dialogTitle(itr->first, dialog, message);
        case Dialog::Stage::article:
            return dialogArticle(itr->first, dialog, message);
    }

    return Shared::error;
}

Shared::Result Module::Publish::dialogAddress(const std::string& jid, Dialog& dialog, std::string_view address) {
    std::string_view node = pop(address, '@');
    if (node.empty() || address.empty()) {
        warn("Malformed address in \"dialog\" method");
        core->send(jid, "The address you've sent is malformed" ADDR ", try again or " CANCEL);

        return Shared::grab;
    }

    dialog.node = node;
    dialog.service = address;
    dialog.stage = Dialog::Stage::title;
    core->send(jid, "Now please provide the title for your publication or " CANCEL);

    return Shared::grab;
}

Shared::Result Module::Publish::dialogTitle(const std::string& jid, Dialog& dialog, std::string_view title) {
    dialog.title = title;
    dialog.stage = Dialog::Stage::article;

    core->send(jid, "Now please provide the body for your publication or " CANCEL);

    return Shared::grab;
}

Shared::Result Module::Publish::dialogArticle(const std::string& jid, Dialog& dialog, std::string_view article) {
    core->send(jid, "Publishing...");

    core->publish(dialog.service, dialog.node, dialog.title, std::string(article));
    dialogs.erase(jid);

    return Shared::success;
}

Shared::Result Module::Publish::to(std::string_view address, std::string_view title, std::string_view body) {
    std::string_view node = pop(address, '@');
    if (node.empty() || address.empty()) {
        warn("Malformed address in \"to\" method");
        return Shared::error;
    }

    try {
        core->publish(std::string(address), std::string(node), std::string(title), std::string(body));
        return Shared::success;
    } catch (const std::exception& e) {
        error("Exception in \"to\" method: " + std::string(e.what()));
    } catch (...) {
        error("Unhandled exception in \"to\" method");
    }

    return Shared::error;
}

Module::Publish::Dialog::Dialog():
    stage(Stage::address),
    node(),
    service(),
    title()
{}

Module::Publish::Dialog::Dialog(Stage stage):
    stage(stage),
    node(),
    service(),
    title()
{}