/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#include "catch2_common.h"
#include "tango/client/ApiUtil.h"

constexpr const char *k_attr_name = "double_attr";
constexpr const Tango::DevDouble k_initial_value = 0.0;

template <class Base>
class ClientCleanup : public Base
{
  public:
    using Base::Base;

    void init_device() { }

    void read_attr(Tango::Attribute &attr)
    {
        attr.set_value(&value);
    }

    static void attribute_factory(std::vector<Tango::Attr *> &attrs)
    {
        attrs.push_back(new TangoTest::AutoAttr<&ClientCleanup::read_attr>(k_attr_name, Tango::DEV_DOUBLE));
        attrs.back()->set_change_event(true, false);
    }

  private:
    Tango::DevDouble value = k_initial_value;
};

TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(ClientCleanup, 1)

SCENARIO("A client can subscribe again after ApiUtil cleanup")
{
    int idlver = GENERATE(TangoTest::idlversion(6));
    GIVEN("a device proxy to a simple IDLv" << idlver << " device")
    {
        TangoTest::Context ctx{"event_client_cleanup", "ClientCleanup", idlver};
        std::shared_ptr<Tango::DeviceProxy> device = ctx.get_proxy();

        REQUIRE(idlver == device->get_idl_version());

        WHEN("we subscribe to an attribute")
        {
            using namespace TangoTest::Matchers;

            TangoTest::CallbackMock<Tango::EventData> callback;
            TangoTest::Subscription sub{device, k_attr_name, Tango::CHANGE_EVENT, &callback};

            require_initial_events(callback, k_initial_value);

            AND_WHEN("we cleanup the ApiUtil object")
            {
                device = nullptr;
                Tango::ApiUtil::cleanup();
                device = ctx.get_proxy();

                THEN("we can subscribe again")
                {
                    using namespace TangoTest::Matchers;

                    TangoTest::CallbackMock<Tango::EventData> callback;
                    TangoTest::Subscription sub{device, k_attr_name, Tango::CHANGE_EVENT, &callback};

                    require_initial_events(callback, k_initial_value);
                }
            }
        }
    }
}

SCENARIO("ApiUtil can be shutdown when configured with push cb model")
{
    GIVEN("the ApiUtil singleton")
    {
        Tango::ApiUtil *au = Tango::ApiUtil::instance();

        WHEN("we set the cb_sub_model to PUSH_CALLBACK")
        {
            au->set_asynch_cb_sub_model(Tango::PUSH_CALLBACK);

            CHECK(au->get_asynch_cb_sub_model() == Tango::PUSH_CALLBACK);

            // We sleep to make sure that the CallBackThread starts waiting on
            // the asyn table.
            std::this_thread::sleep_for(std::chrono::milliseconds{1});

            THEN("we can shutdown the ApiUtil singleton without hanging")
            {
                Tango::ApiUtil::cleanup();
                CHECK(!Tango::ApiUtil::is_started());
            }
        }
    }
}
