/*
 * Copyright (C) 2016 Canonical Ltd.
 * 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 "../../src/rclone_error.h"
#include <utils/ExpectThrow.h>

#include <gtest/gtest.h>
#include <QBuffer>
#include <QByteArray>
#include <QCoreApplication>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <lomiri/storage/provider/Exceptions.h>

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

class FakeReply : public QNetworkReply
{
public:
    FakeReply(int status, QString const& reason, QByteArray const& body)
    {
        setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
        setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reason);
        setHeader(QNetworkRequest::ContentTypeHeader,
                  QStringLiteral("application/json"));
        setHeader(QNetworkRequest::ContentLengthHeader, body.size());
        buffer_.setData(body);
        buffer_.open(QIODevice::ReadOnly);
        open(QIODevice::ReadOnly);
        setFinished(true);
    }

    FakeReply(QNetworkReply::NetworkError code, QString const& message)
    {
        setError(code, message);
        setFinished(true);
    }

    void abort() override { close(); }
    qint64 bytesAvailable() const override
    {
        return QNetworkReply::bytesAvailable() + buffer_.bytesAvailable();
    }
    bool isSequential() const override { return true; }
    qint64 size() const override { return buffer_.size(); }
    qint64 readData(char* data, qint64 length) override
    {
        return buffer_.read(data, length);
    }

private:
    QBuffer buffer_;
};

QByteArray json_error(char const* message, int status)
{
    return QByteArray("{\"error\":\"") + message +
           "\",\"status\":" + QByteArray::number(status) + "}";
}

TEST(RcloneError, network_error)
{
    FakeReply reply(QNetworkReply::HostNotFoundError, "Host not found");
    auto e = expect_throw<RemoteCommsException>(
        translate_rclone_error(&reply, QByteArray()));
    EXPECT_EQ("Host not found", e.error_message());
}

TEST(RcloneError, not_found_404)
{
    FakeReply reply(404, "Not Found", json_error("object not found", 404));
    auto e = expect_throw<NotExistsException>(
        translate_rclone_error(&reply, json_error("object not found", 404),
                               "foo.txt"));
    EXPECT_EQ("foo.txt", e.key());
}

// rc daemon rejecting credentials
TEST(RcloneError, rc_auth_failure_is_resource_error)
{
    auto body401 = json_error("Unauthorized", 401);
    FakeReply reply401(401, "Unauthorized", body401);
    expect_throw<ResourceException>(translate_rclone_error(&reply401, body401));

    auto body403 = json_error("authentication must be set up on the rc server", 403);
    FakeReply reply403(403, "Forbidden", body403);
    expect_throw<ResourceException>(translate_rclone_error(&reply403, body403));
}

// missing rc method / caller bug
TEST(RcloneError, unknown_method_404_is_not_notexists)
{
    auto body = json_error("couldn't find method \\\"operations/bogus\\\"", 404);
    FakeReply reply(404, "Not Found", body);
    expect_throw<UnknownException>(translate_rclone_error(&reply, body));
}

TEST(RcloneError, permission_in_500)
{
    auto body = json_error("permission denied", 500);
    FakeReply reply(500, "Internal Server Error", body);
    expect_throw<PermissionException>(translate_rclone_error(&reply, body));
}

TEST(RcloneError, not_found_in_500)
{
    auto body = json_error("directory not found", 500);
    FakeReply reply(500, "Internal Server Error", body);
    expect_throw<NotExistsException>(
        translate_rclone_error(&reply, body, "sub/"));
}

TEST(RcloneError, exists_in_500)
{
    auto body = json_error("destination already exists", 500);
    FakeReply reply(500, "Internal Server Error", body);
    auto e = expect_throw<ExistsException>(
        translate_rclone_error(&reply, body, "folder/"));
    EXPECT_EQ("folder", e.name());
}

TEST(RcloneError, quota_in_500)
{
    auto body = json_error("quota exceeded", 500);
    FakeReply reply(500, "Internal Server Error", body);
    expect_throw<QuotaException>(translate_rclone_error(&reply, body));
}

TEST(RcloneError, generic_500)
{
    auto body = json_error("some backend hiccup", 500);
    FakeReply reply(500, "Internal Server Error", body);
    auto e = expect_throw<RemoteCommsException>(
        translate_rclone_error(&reply, body));
    EXPECT_EQ("some backend hiccup", e.error_message());
}

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
