#pragma once

#include "Message.h"
#include "Node.h"
#include "defs.h"

#include <expected>
#include <mutex>
#include <sdi_toolBox/dateTime/timer.h>
#include <set>
#include <unordered_map>

namespace dBus
{
//--------------------------------------------------------------
class Bus
{
  public:
  struct MonitoringData
  {
    size_t globalMessageCount{ 0 };     // Total count of messages posted to the bus
    double frequencyHz = { 0 };         // Current frequency of messages being posted to the bus (calculated as messages per second)

    size_t                    currentMessageCount{ 0 };     // Current count of messages on the bus
    std::chrono::microseconds maxAge{};                     // Maximum age of messages on the bus
  };

  public:
  Bus();                                            // Default constructor
  virtual ~Bus()                     = default;     // Default destructor
  Bus(const Bus &obj)                = delete;      // Copy constructor
  Bus(Bus &&obj) noexcept            = delete;      // Move constructor
  Bus &operator=(const Bus &obj)     = delete;      // Copy assignment operator
  Bus &operator=(Bus &&obj) noexcept = delete;      // Move assignment operator

  [[nodiscard]] TimePoint getStartTimestamp() const;     // Get the timestamp of when the bus started

  // Registration and unregistration of listeners
  void               subscribe(const HashID &eventType, Node *node);                    // Subscribe a listener to a specific event type
  void               unsubscribe(const HashID &eventType, Node *node);                  // Unsubscribe a listener from a specific event type
  void               unsubscribeAll(Node *node);                                        // Unsubscribe a listener from all event types
  [[nodiscard]] bool isSubscribed(const HashID &eventType, const Node *node) const;     // Check if a listener is subscribed to a specific event type

  // Broadcast management
  void               subscribeToBroadcast(Node *node);              // Subscribe a node to receive all messages (broadcast mode)
  void               unsubscribeFromBroadcast(Node *node);          // Unsubscribe a node from broadcast mode
  [[nodiscard]] bool isSubscribedToBroadcast(Node *node) const;     // Check if a node is subscribed to broadcast mode

  // Data transmission
  bool post(const std::shared_ptr<Message> &message);     // Post a message to the bus

  // Monitoring
  MonitoringData getMonitoringData() const;                        // Get the current monitoring data of the bus (message count, frequency, etc.)
  void           setMessageAge(std::chrono::microseconds age);     // Set the age of the message

  protected:
  TimePoint    m_startTimestamp;       // Timestamp of when the bus started
  std::jthread m_monitoringThread;     // Thread for monitoring the performance of the bus

  struct
  {
    mutable std::mutex                              mtx;                  // Mutex for thread-safe access to the nodes map
    std::unordered_map<HashID, std::vector<Node *>> nodes;                // Map of message type IDs to lists of subscribed nodes
    std::set<Node *>                                broadcastNodes;       // Set of nodes subscribed to receive all messages (broadcast mode)
    std::set<Node *>                                activeNodesCache;     // Cache of active nodes for quick access during message routing
  } m_routingTable;

  struct
  {
    mutable std::mutex mtx;     // Mutex for thread-safe access to the monitoring data
    MonitoringData     data;

    sdi_toolBox::dateTime::Timer frequencyTimer;             // Timer for calculating message frequency
    size_t                       messageCounter = { 0 };     // Counter for calculating message frequency
  } m_monitoring;

  private:
  bool isSubscribedEverywhere(Node *node) const;     // Check if a node is subscribed to any message type (used for managing the active nodes cache)
  void recordIncoming();                             // Incoming messages for monitoring purposes
  void updateMonitoringData();                       // Update the monitoring data of the bus (message count, frequency, etc.)
};
//--------------------------------------------------------------
}     // namespace dBus
