// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#pragma once

#include <grpcpp/completion_queue.h>
#include <grpcpp/server_context.h>

#include <atomic>
#include <functional>

class AbstractTag
{
public:
    explicit AbstractTag() = default;
    virtual ~AbstractTag() = default;

    AbstractTag(const AbstractTag &) = delete;
    AbstractTag &operator=(const AbstractTag &) = delete;

    AbstractTag(AbstractTag &&) = default;
    AbstractTag &operator=(AbstractTag &&) = default;

    virtual void process(bool ok) = 0;
};

class CallbackTag : public AbstractTag
{
public:
    enum Operation { Proceed, Delete };
    using Function = std::function<Operation(bool)>;
    explicit CallbackTag(Function fn) : mFn(std::move(fn)) { }
    void process(bool ok) override
    {
        if (mFn(ok) == Delete)
            delete this;
    }

private:
    Function mFn;
};

class VoidTag final : public CallbackTag
{
public:
    VoidTag() : CallbackTag([](bool) { return Delete; }) { }
};

template <typename Data>
class DeleteTag final : public CallbackTag
{
public:
    explicit DeleteTag(Data *data) : CallbackTag([](bool) { return Delete; }), data(data)
    {
        assert(this->data);
    }
    ~DeleteTag() { delete data; }

private:
    Data *data;
};

class AbstractRpcTag : public AbstractTag
{
public:
    AbstractRpcTag()
    {
        mContext.AsyncNotifyWhenDone(new CallbackTag([this](bool ok) {
            if (ok && mContext.IsCancelled())
                mIsCancelled = true;
            return CallbackTag::Delete;
        }));
    }

    virtual void start(grpc::ServerCompletionQueue *cq) = 0;

    const grpc::ServerContext &context() const { return mContext; }
    grpc::ServerContext &context() { return mContext; }
    [[nodiscard]] bool isCancelled() const noexcept { return mIsCancelled.load(); }

private:
    std::atomic<bool> mIsCancelled = false;
    grpc::ServerContext mContext;
};
