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

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

    void init_device() override { }

    void read_attr(Tango::Attribute &) override
    {
        /* Do nothing */
    }

    void set_value()
    {
        auto &att = Base::get_device_attr()->get_attr_by_name("attr_read_no_set");
        value = 1.0;
        att.set_value(&value);
    }

    static void attribute_factory(std::vector<Tango::Attr *> &attrs)
    {
        attrs.push_back(new TangoTest::AutoAttr<&AttrReadNoSet::read_attr>("attr_read_no_set", Tango::DEV_DOUBLE));
        Tango::UserDefaultAttrProp props;
        props.set_max_alarm("10.0");
        attrs.back()->set_default_properties(props);
    }

    static void command_factory(std::vector<Tango::Command *> &cmds)
    {
        cmds.push_back(new TangoTest::AutoCommand<&AttrReadNoSet::set_value>("SetValue"));
    }

  private:
    Tango::DevDouble value;
};

TANGO_TEST_AUTO_DEV_TMPL_INSTANTIATE(AttrReadNoSet, 3)

SCENARIO("Not setting a value")
{
    int idlver = GENERATE(TangoTest::idlversion(3));
    GIVEN("a device proxy to a simple IDLv" << idlver << " device")
    {
        TangoTest::Context ctx{"attr_read_no_set", "AttrReadNoSet", idlver};
        auto device = ctx.get_proxy();

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

        WHEN("we read a do nothing attribute")
        {
            std::string att{"attr_read_no_set"};

            Tango::DeviceAttribute da;
            REQUIRE_NOTHROW(da = device->read_attribute(att));

            THEN("we get a API_AttrValueNotSet exception")
            {
                using namespace TangoTest::Matchers;
                using namespace Catch::Matchers;

                double val_read{};
                REQUIRE_THROWS_MATCHES(
                    da >> val_read, Tango::DevFailed, ErrorListMatches(AnyMatch(Reason(Tango::API_AttrValueNotSet))));
            }
        }

        WHEN("we set an attribute value out-of-band")
        {
            REQUIRE_NOTHROW(device->command_inout("SetValue"));

            AND_WHEN("we read the do nothing attribute")
            {
                std::string att{"attr_read_no_set"};

                Tango::DeviceAttribute da;
                REQUIRE_NOTHROW(da = device->read_attribute(att));

                THEN("we get a API_AttrValueNotSet exception")
                {
                    using namespace TangoTest::Matchers;

                    double val_read{};
                    REQUIRE_THROWS_MATCHES(
                        da >> val_read, Tango::DevFailed, FirstErrorMatches(Reason(Tango::API_AttrValueNotSet)));
                }
            }
        }
    }
}
