#pragma once

#include <chrono>
#include <cstdint>
#include <format>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>

namespace dBus
{
using HashID    = uint32_t;
using TimePoint = std::chrono::steady_clock::time_point;

constexpr auto DEFAULT_TIMEOUT = std::chrono::milliseconds(100);

//--------------------------------------------------------------
// Hash function for IDs using FNV-1a algorithm
constexpr HashID makeID(const std::string_view id) noexcept
{
  constexpr uint32_t FNV_offset_basis = 2166136261u;
  constexpr uint32_t FNV_prime        = 16777619u;

  uint32_t hash_value = FNV_offset_basis;
  for (const char c : id)
  {
    hash_value ^= static_cast<uint32_t>(static_cast<uint8_t>(c));
    hash_value *= FNV_prime;
  }
  return hash_value;
}
//--------------------------------------------------------------
template<typename T, typename = void>
struct has_to_string : std::false_type
{
};
template<typename T>
struct has_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>>
  : std::true_type
{
};
template<typename T>
constexpr bool has_to_string_v = has_to_string<T>::value;
//--------------------------------------------------------------
enum class ReturnStatus : uint8_t
{
  Success = 0,
  Timeout,
  NoSubscribers,
  Error,
  Exception,
};
//--------------------------------------------------------------
enum class MessageCategory : uint32_t
{
  None     = 0,
  Log      = 1 << 0,
  Hardware = 1 << 1,
  Dataset  = 1 << 2,
  Database = 1 << 3,
  System   = 1 << 4,
  UI       = 1 << 5,
  All      = 0xFFFFFFFF
};

// Convert LogCategory to a string representation
inline std::string logCategoryToString(const MessageCategory &cat)
{
  static const std::unordered_map<MessageCategory, std::string> names = {
    { MessageCategory::None, "None" },
    { MessageCategory::Log, "Log" },
    { MessageCategory::Hardware, "Hardware" },
    { MessageCategory::Dataset, "Dataset" },
    { MessageCategory::Database, "Database" },
    { MessageCategory::System, "System" },
    { MessageCategory::UI, "UI" },
    { MessageCategory::All, "All" }
  };

  const auto it = names.find(cat);
  return (it != names.end()) ? it->second : "Composite/Unknown";
}

// Bitwise OR operator for LogCategory to allow combining categories
inline MessageCategory operator|(const MessageCategory lhs, const MessageCategory rhs)
{
  return static_cast<MessageCategory>(
    static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
}

// Compound assignment operator for LogCategory
inline MessageCategory &operator|=(MessageCategory &lhs, const MessageCategory rhs)
{
  lhs = lhs | rhs;
  return lhs;
}

// Bitwise AND operator for LogCategory to check if a category is included in a combination
inline bool operator&(const MessageCategory lhs, const MessageCategory rhs)
{
  return (static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)) != 0;
}
//--------------------------------------------------------------
}     // namespace dBus
