// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2020-2021, The Monero Project.

#include "daemonrpc.h"

DaemonRpc::DaemonRpc(QObject *parent, UtilsNetworking *network, QString daemonAddress)
        : QObject(parent)
        , m_network(network)
        , m_daemonAddress(std::move(daemonAddress))
{
}

void DaemonRpc::sendRawTransaction(const QString &tx_as_hex, bool do_not_relay, bool do_sanity_checks) {
    QJsonObject req;
    req["tx_as_hex"] = tx_as_hex;
    req["do_not_relay"] = do_not_relay;
    req["do_sanity_checks"] = do_sanity_checks;

    QString url = QString("%1/send_raw_transaction").arg(m_daemonAddress);
    QNetworkReply *reply = m_network->postJson(url, req);
    connect(reply, &QNetworkReply::finished, std::bind(&DaemonRpc::onResponse, this, reply, Endpoint::SEND_RAW_TRANSACTION));
}

void DaemonRpc::getTransactions(const QStringList &txs_hashes, bool decode_as_json, bool prune) {
    QJsonObject req;
    req["txs_hashes"] = QJsonArray::fromStringList(txs_hashes);
    req["decode_as_json"] = decode_as_json;
    req["prune"] = prune;

    QString url = QString("%1/get_transactions").arg(m_daemonAddress);
    QNetworkReply *reply = m_network->postJson(url, req);
    connect(reply, &QNetworkReply::finished, std::bind(&DaemonRpc::onResponse, this, reply, Endpoint::GET_TRANSACTIONS));
}

void DaemonRpc::onResponse(QNetworkReply *reply, Endpoint endpoint) {
    const auto ok = reply->error() == QNetworkReply::NoError;
    const auto err = reply->errorString();

    QByteArray data = reply->readAll();
    QJsonObject obj;
    if (!data.isEmpty() && Utils::validateJSON(data)) {
        auto doc = QJsonDocument::fromJson(data);
        obj = doc.object();
    }
    else if (!ok) {
        emit ApiResponse(DaemonResponse(false, endpoint, err));
        return;
    }
    else {
        emit ApiResponse(DaemonResponse(false, endpoint, "Invalid response from daemon"));
        return;
    }

    if (obj.value("status").toString() != "OK") {
        QString failedMsg;
        switch (endpoint) {
            case SEND_RAW_TRANSACTION:
                failedMsg = this->onSendRawTransactionFailed(obj);
                break;
            default:
                failedMsg = obj.value("status").toString();
        }

        emit ApiResponse(DaemonResponse(false, endpoint, failedMsg, obj));
        return;
    }

    reply->deleteLater();
    emit ApiResponse(DaemonResponse(true, endpoint, "", obj));
}

QString DaemonRpc::onSendRawTransactionFailed(const QJsonObject &obj) {
    QString message = [&obj]{
        if (obj.value("double_spend").toBool())
            return "Transaction is a double spend";
        if (obj.value("fee_too_low").toBool())
            return "Fee is too low";
        if (obj.value("invalid_input").toBool())
            return "Output is invalid";
        if (obj.value("low_mixin").toBool())
            return "Mixin count is too low";
        if (obj.value("overspend").toBool())
            return "Transaction uses more money than available";
        if (obj.value("too_big").toBool())
            return "Transaction size is too big";
        return "Daemon returned an unknown error";
    }();

    return QString("Transaction failed: %1").arg(message);
}

void DaemonRpc::setDaemonAddress(const QString &daemonAddress) {
    m_daemonAddress = daemonAddress;
}

void DaemonRpc::setNetwork(UtilsNetworking *network) {
    m_network = network;
}