/*
 * Copyright (C) 2026 UBports Foundation.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "rclone_ops.h"

#include "RcloneClient.h"
#include "RcloneItem.h"
#include "item_id.h"

#include <QJsonObject>
#include <QString>
#include <lomiri/storage/provider/Exceptions.h>

using namespace std;
using namespace lomiri::storage::provider;

void verify_not_exists(shared_ptr<RcloneClient> const& client,
                       shared_ptr<boost::promise<Item>> const& promise,
                       string const& fs, string const& remote,
                       string const& item_id, string const& name,
                       function<void()> proceed, ExistsError on_exists)
{
    client->call(QStringLiteral("operations/stat"),
                 RcloneClient::make_params(fs, remote), name,
                 [promise, item_id, name, proceed, on_exists](
                     QJsonObject const& result,
                     boost::exception_ptr const& error) {
        if (error)
        {
            promise->set_exception(error);
            return;
        }
        if (stat_exists(result))
        {
            promise->set_exception(on_exists == ExistsError::Conflict
                ? boost::copy_exception(
                      ConflictException(item_id + " already exists"))
                : boost::copy_exception(
                      ExistsException(name + " already exists", item_id, name)));
            return;
        }
        proceed();
    });
}

void fetch_item_metadata(shared_ptr<RcloneClient> const& client,
                         shared_ptr<boost::promise<Item>> const& promise,
                         string const& fs, string const& remote,
                         string const& item_id)
{
    client->call(QStringLiteral("operations/stat"),
                 RcloneClient::make_params(fs, remote), item_id,
                 [promise, item_id](QJsonObject const& result,
                                    boost::exception_ptr const& error) {
        if (error)
        {
            promise->set_exception(error);
            return;
        }
        if (!stat_exists(result))
        {
            promise->set_exception(boost::copy_exception(
                NotExistsException(item_id + " does not exist", item_id)));
            return;
        }

        QJsonObject item_json = result.value(QStringLiteral("item")).toObject();
        if (item_id == ".")
        {
            promise->set_value(make_root_item(item_json));
            return;
        }

        bool is_dir = item_json.value(QStringLiteral("IsDir")).toBool();
        string id = make_child_id(parent_of(item_id), name_of(item_id), is_dir);
        promise->set_value(make_item(item_json, id));
    });
}

void verify_etag(shared_ptr<RcloneClient> const& client,
                 string const& fs, string const& remote, string const& item_id,
                 string const& expected_etag, RcloneClient::RcCallback cb)
{
    client->call(QStringLiteral("operations/stat"),
                 RcloneClient::make_params(fs, remote), item_id,
                 [item_id, expected_etag, cb](QJsonObject const& result,
                                              boost::exception_ptr const& error) {
        if (error)
        {
            cb(QJsonObject(), error);
            return;
        }
        if (!stat_exists(result))
        {
            cb(QJsonObject(), boost::copy_exception(
                NotExistsException(item_id + " does not exist", item_id)));
            return;
        }
        QJsonObject item_json = result.value(QStringLiteral("item")).toObject();
        if (make_etag(item_json) != expected_etag)
        {
            cb(QJsonObject(), boost::copy_exception(ConflictException(
                "etag mismatch: " + item_id + " was modified")));
            return;
        }
        cb(result, boost::exception_ptr());
    });
}

void create_remote(shared_ptr<RcloneClient> const& client, string const& name,
                   string const& type, map<string, string> const& params,
                   function<void(boost::exception_ptr const& error)> cb)
{
    QJsonObject parameters;
    for (auto const& param : params)
    {
        parameters.insert(QString::fromStdString(param.first),
                          QString::fromStdString(param.second));
    }

    QJsonObject call_params{
        {QStringLiteral("name"), QString::fromStdString(name)},
        {QStringLiteral("type"), QString::fromStdString(type)},
        {QStringLiteral("parameters"), parameters},
        {QStringLiteral("opt"), QJsonObject{{QStringLiteral("obscure"), true}}},
    };

    client->call(QStringLiteral("config/create"), call_params,
                 [cb](QJsonObject const&, boost::exception_ptr const& error) {
        cb(error);
    });
}

