/*
 * SPDX-FileCopyrightText: 2025 Copyright contributors to the cppTango project
 *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */
#ifndef TANGO_CLIENT_EVENTCONSUMERKEEPALIVETHREAD_H
#define TANGO_CLIENT_EVENTCONSUMERKEEPALIVETHREAD_H

#include <tango/client/DeviceData.h>
#include <tango/common/omnithread_wrapper.h>
#include <tango/common/tango_const.h>

#include <COS/CosNotification.hh>
#include <COS/CosNotifyChannelAdmin.hh>
#include <COS/CosNotifyComm.hh>

#include <cstdint>
#include <map>
#include <memory>
#include <optional>

namespace Tango
{

class EventConsumer;
class DeviceProxy;
class CallBack;
class EventQueue;
class TangoMonitor;

/********************************************************************************
 *                                                                                 *
 *                         KeepAliveThCmd class                                    *
 *                                                                                 *
 *******************************************************************************/

class KeepAliveThCmd : public omni_mutex
{
  public:
    KeepAliveThCmd() :

        cond(this)
    {
    }

    bool cmd_pending{false};   // The new command flag
    KeepAliveCmdCode cmd_code; // The command code
    omni_condition cond;
};

/********************************************************************************
 *                                                                                 *
 *                         Map structures                                          *
 *                                                                                 *
 *******************************************************************************/

typedef struct event_not_connected
{
    DeviceProxy *device;
    std::string attribute;
    EventType event_type;
    std::string event_name;
    int event_id;
    CallBack *callback;
    EventQueue *ev_queue;
    std::vector<std::string> filters;
    time_t last_heartbeat;
    std::string prefix;
    EventSubMode event_sub_mode;
} EventNotConnected;

//------------------------ Event Callback related info --------------------------------------

struct ReceivedFromAdmin
{
    std::string event_name;
    std::string channel_name;
};

typedef struct event_subscribe
{
    EventQueue *ev_queue;
    CallBack *callback;
    int id;
    DeviceProxy *device;
} EventSubscribeStruct;

typedef struct event_callback_base
{
    std::string obj_name;
    std::string event_name;
    std::string channel_name;
    std::string fully_qualified_event_name;
    time_t last_subscribed{0};
    TangoMonitor *callback_monitor;
    std::vector<EventSubscribeStruct> callback_list;
    bool alias_used;
    DeviceProxy &get_device_proxy();
} EventCallBackBase;

typedef struct event_callback_zmq
{
    DevLong device_idl;
    DevULong ctr;
    std::string endpoint;
    bool discarded_event;
    bool fwd_att;
} EventCallBackZmq;

typedef struct event_callback : public EventCallBackBase, public EventCallBackZmq
{
    std::string filter_constraint;
    CosNotifyFilter::FilterID filter_id;
    bool filter_ok = false;
    std::string client_attribute_name;
    ReceivedFromAdmin received_from_admin;

    // For monitoring
    std::uint64_t event_count{};
    std::uint64_t discarded_event_count{};
    std::uint64_t missed_event_count{};

    std::string get_client_attribute_name()
    {
        return client_attribute_name;
    }
} EventCallBackStruct;

//------------------------ Event Channel related info --------------------------------------

typedef struct event_channel_base
{
    std::shared_ptr<DeviceProxy> adm_device_proxy;
    std::string full_adm_name;
    time_t last_subscribed;
    time_t last_heartbeat;
    bool heartbeat_skipped;
    std::shared_ptr<TangoMonitor> channel_monitor;
    ChannelType channel_type;
} EventChannelBase;

typedef struct event_channel_zmq
{
    std::string endpoint;
    size_t valid_endpoint;
} EventChannelZmq;

typedef struct channel_struct : public EventChannelBase, public EventChannelZmq
{
    CosNotifyChannelAdmin::EventChannel_var eventChannel;
    CosNotifyChannelAdmin::StructuredProxyPushSupplier_var structuredProxyPushSupplier;
    CosNotifyFilter::FilterID heartbeat_filter_id;
    std::string notifyd_host;
    bool event_system_failed;
    long has_notifd_closed_the_connection;
} EventChannelStruct;

typedef std::map<std::string, EventChannelStruct>::iterator EvChanIte;
typedef std::map<std::string, EventCallBackStruct>::iterator EvCbIte;

/********************************************************************************
 *                                                                                 *
 *                         EventConsumerKeepAliveThread class                        *
 *                                                                                 *
 *******************************************************************************/

// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)
class EventConsumerKeepAliveThread : public omni_thread
{
  public:
    EventConsumerKeepAliveThread(const EventConsumer &);

    EventConsumerKeepAliveThread(KeepAliveThCmd &cmd) :
        shared_cmd(cmd)
    {
    }

    void start()
    {
        start_undetached();
    }

    void async_success_notification(const std::vector<EventNotConnected>::iterator &vpos, time_t now);

    void subscription_feedback(const std::vector<EventNotConnected>::iterator &,
                               std::optional<DevFailed> e,
                               EventReason event_reason,
                               const time_t &);
    void fwd_not_conected_event(PointerWithLock<EventConsumer> &);

  protected:
    KeepAliveThCmd &shared_cmd;

  private:
    void *run_undetached(void *arg) override;
    bool reconnect_to_channel(const EvChanIte &, PointerWithLock<EventConsumer> &);
    void reconnect_to_event(const EvChanIte &, PointerWithLock<EventConsumer> &);
    void re_subscribe_event(const EvCbIte &, const EvChanIte &);

    bool reconnect_to_zmq_channel(const EvChanIte &, PointerWithLock<EventConsumer> &, DeviceData &);
    void reconnect_to_zmq_event(const EvChanIte &, PointerWithLock<EventConsumer> &, DeviceData &);
    void not_conected_event(PointerWithLock<EventConsumer> &, time_t, PointerWithLock<EventConsumer> &);
    void confirm_subscription(PointerWithLock<EventConsumer> &,
                              const std::map<std::string, EventChannelStruct>::iterator &);
    void main_reconnect(PointerWithLock<EventConsumer> &,
                        PointerWithLock<EventConsumer> &,
                        std::map<std::string, EventCallBackStruct>::iterator &,
                        const std::map<std::string, EventChannelStruct>::iterator &);
    void re_subscribe_after_reconnect(PointerWithLock<EventConsumer> &,
                                      PointerWithLock<EventConsumer> &,
                                      const std::map<std::string, EventCallBackStruct>::iterator &,
                                      const std::map<std::string, EventChannelStruct>::iterator &,
                                      const std::string &);

    ~EventConsumerKeepAliveThread() override = default;
};

} // namespace Tango

#endif
