/**
 * @file src/system_tray.cpp
 * @brief Definitions for the system tray icon and notification system.
 */
// macros
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1

  /**
   * @def TRAY_ICON
   * @brief Path to the default system tray icon.
   */
  #define TRAY_ICON WEB_DIR "images/logo-sunshine.svg"
  /**
   * @def TRAY_ICON_PLAYING
   * @brief Path to the system tray icon used while streaming.
   */
  #define TRAY_ICON_PLAYING WEB_DIR "images/sunshine-playing.svg"
  /**
   * @def TRAY_ICON_PAUSING
   * @brief Path to the system tray icon used while streaming is paused.
   */
  #define TRAY_ICON_PAUSING WEB_DIR "images/sunshine-pausing.svg"
  /**
   * @def TRAY_ICON_LOCKED
   * @brief Path to the system tray icon used for pairing requests.
   */
  #define TRAY_ICON_LOCKED WEB_DIR "images/sunshine-locked.svg"
  /**
   * @def TRAY_ICON_VIRTUALHID
   * @brief Path to the Virtual HID Driver notification icon.
   */
  #define TRAY_ICON_VIRTUALHID WEB_DIR "images/logo-libvirtualhid.svg"

  #if defined(_WIN32)
    /**
     * @def WIN32_LEAN_AND_MEAN
     * @brief Macro for WIN32 LEAN AND MEAN.
     */
    #define WIN32_LEAN_AND_MEAN
    #include <AccCtrl.h>
    #include <AclAPI.h>
  #elif defined(__APPLE__) || defined(__MACH__)
    #include <CoreFoundation/CoreFoundation.h>
    #include <dispatch/dispatch.h>
  #endif

  // standard includes
  #include <array>
  #include <atomic>
  #include <chrono>
  #include <csignal>
  #include <cstddef>
  #include <filesystem>
  #include <format>
  #include <functional>
  #include <mutex>
  #include <optional>
  #include <string>
  #include <string_view>
  #include <system_error>
  #include <thread>
  #include <unordered_map>
  #include <utility>

  // lib includes
  #include <boost/filesystem.hpp>
  #include <boost/process/v1/environment.hpp>
  #include <tray.h>
  #ifdef _WIN32
    #include <libvirtualhid/license.hpp>
  #endif

  // local includes
  #include "confighttp.h"
  #include "display_device.h"
  #include "logging.h"
  #include "platform/common.h"
  #include "process.h"
  #include "src/entry_handler.h"
  #include "system_tray.h"
  #ifdef _WIN32
    #include "platform/windows/utf_utils.h"
  #endif

using namespace std::literals;

// system_tray namespace
namespace system_tray {
  namespace {
    /**
     * @brief Access the process-wide tray initialization state.
     *
     * @return Atomic initialization flag shared by tray operations.
     */
    std::atomic_bool &tray_initialized_state() {
      static std::atomic_bool initialized {false};
      return initialized;
    }

    /**
     * @brief Access the mutex that serializes tray data and UI changes.
     *
     * @return Mutex shared by tray operations.
     */
    std::mutex &tray_state_mutex() {
      static std::mutex mutex;
      return mutex;
    }

    /**
     * @brief Access the owned tray worker thread.
     *
     * @return Worker thread used by the threaded tray lifecycle.
     */
    std::jthread &tray_worker_thread() {
      static std::jthread worker;
      return worker;
    }

    /**
     * @brief Hash string-like resource paths without allocating temporary strings.
     */
    struct transparent_string_hash_t {
      using is_transparent = void;  ///< Enable heterogeneous unordered-map lookup.

      /**
       * @brief Hash a string view.
       *
       * @param value String view to hash.
       * @return Hash value for the supplied text.
       */
      std::size_t operator()(const std::string_view value) const noexcept {
        return std::hash<std::string_view> {}(value);
      }
    };

  #ifdef _WIN32
    /**
     * @brief Access storage for dynamic Virtual HID Driver license menu labels.
     *
     * @return Persistent string storage backing the tray menu label pointers.
     */
    std::array<std::string, 11> &virtualhid_license_menu_text_storage() {
      static std::array<std::string, 11> menu_text;
      return menu_text;
    }
  #endif
  }  // namespace

  #ifdef _WIN32
  constexpr auto LIBVIRTUALHID_RELEASES_URL = "https://github.com/LizardByte/libvirtualhid/releases/latest"sv;  ///< Latest Virtual HID Driver release.
  static std::array<struct tray_menu, 6> virtualhid_benefits_menu {{
    {.text = "Xbox One, Xbox Series, DualSense (DS5), Switch Pro, and Generic", .disabled = 1},
    {.text = "Motion, touchpads, LEDs, and adaptive triggers where supported", .disabled = 1},
    {.text = "Actively developed and supported by LizardByte", .disabled = 1},
    {.text = "-"},
    {.text = "Open License Settings", .cb = tray_virtualhid_license_cb},
    {},
  }};  ///< Persistent Virtual HID Driver benefits shown in the notification area.
  #endif

  void tray_open_ui_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Opening UI from system tray"sv;
    launch_ui();
  }

  void tray_donate_github_cb([[maybe_unused]] struct tray_menu *item) {
    platf::open_url("https://github.com/sponsors/LizardByte");
  }

  void tray_donate_patreon_cb([[maybe_unused]] struct tray_menu *item) {
    platf::open_url("https://www.patreon.com/LizardByte");
  }

  void tray_donate_paypal_cb([[maybe_unused]] struct tray_menu *item) {
    platf::open_url("https://www.paypal.com/paypalme/ReenigneArcher");
  }

  #ifdef _WIN32
  void tray_virtualhid_license_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Opening Virtual HID Driver license settings from system tray"sv;
    launch_ui("/troubleshooting#virtualhid-license");
  }

  void tray_virtualhid_download_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Opening Virtual HID Driver download from system tray"sv;
    platf::open_url(std::string {LIBVIRTUALHID_RELEASES_URL});
  }
  #endif

  /**
   * @brief Forwards Qt log messages to Sunshine's BOOST_LOG logger.
   * @param level Log level: 0=debug, 1=info, 2=warning, 3=error.
   * @param msg The message string from Qt.
   */
  static void qt_log_to_boost(int level, const char *msg) {
    if (msg == nullptr) {
      return;
    }
    switch (level) {
      case 0:
        BOOST_LOG(debug) << "Qt: " << msg;
        break;
      case 1:
        BOOST_LOG(info) << "Qt: " << msg;
        break;
      case 2:
        BOOST_LOG(warning) << "Qt: " << msg;
        break;
      default:
        BOOST_LOG(error) << "Qt: " << msg;
        break;
    }
  }

  void tray_reset_display_device_config_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Resetting display device config from system tray"sv;

    std::ignore = display_device::reset_persistence();
  }

  void tray_restart_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Restarting from system tray"sv;

    platf::restart();
  }

  void tray_quit_cb([[maybe_unused]] struct tray_menu *item) {
    BOOST_LOG(info) << "Quitting from system tray"sv;

  #ifdef _WIN32
    // If we're running in a service, return a special status to
    // tell it to terminate too, otherwise it will just respawn us.
    if (GetConsoleWindow() == nullptr) {
      lifetime::exit_sunshine(ERROR_SHUTDOWN_IN_PROGRESS, true);
      return;
    }
  #endif

    lifetime::exit_sunshine(0, true);
  }

  #ifdef _WIN32
  /**
   * @brief Create the initial Virtual HID Driver license submenu.
   *
   * @return Menu storage with a checking state, benefits, license settings, and driver download.
   */
  std::array<struct tray_menu, 11> initial_virtualhid_license_menu() {
    std::array<struct tray_menu, 11> menu {};
    menu[0] = {.text = "Status: Checking", .disabled = 1};
    menu[1] = {.text = "-"};
    menu[2] = {.text = "Open License Settings", .cb = tray_virtualhid_license_cb};
    menu[3] = {.text = "Benefits over ViGEmBus", .submenu = virtualhid_benefits_menu.data()};
    menu[4] = {.text = "Download Virtual HID Driver", .cb = tray_virtualhid_download_cb};
    return menu;
  }

  static auto virtualhid_license_menu = initial_virtualhid_license_menu();  ///< Virtual HID Driver license submenu.
  #endif

  // Tray menu
  static struct tray tray = {
    .icon = TRAY_ICON,
    .tooltip = PROJECT_NAME,
    .menu =
      (struct tray_menu[]) {
        // Tray menu labels currently use the project's English source strings.
        {.text = "Open Sunshine", .cb = tray_open_ui_cb},
        {.text = "-"},
  #ifdef _WIN32
        {.text = "Virtual HID Driver", .submenu = virtualhid_license_menu.data()},
        {.text = "-"},
  #endif
        {.text = "Donate",
         .submenu =
           (struct tray_menu[]) {
             {.text = "GitHub Sponsors", .cb = tray_donate_github_cb},
             {.text = "Patreon", .cb = tray_donate_patreon_cb},
             {.text = "PayPal", .cb = tray_donate_paypal_cb},
             {.text = nullptr}
           }},
        {.text = "-"},
  // Currently display device settings are only supported on Windows
  #ifdef _WIN32
        {.text = "Reset Display Device Config", .cb = tray_reset_display_device_config_cb},
  #endif
        {.text = "Restart", .cb = tray_restart_cb},
        {.text = "Quit", .cb = tray_quit_cb},
        {.text = nullptr}
      },
  #ifdef _WIN32
    .iconPathCount = 5,
    .allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING, TRAY_ICON_VIRTUALHID},
  #else
    .iconPathCount = 4,
    .allIconPaths = {TRAY_ICON, TRAY_ICON_LOCKED, TRAY_ICON_PLAYING, TRAY_ICON_PAUSING},
  #endif
  };

  #ifdef SUNSHINE_TESTS
  const struct tray &tray_data_for_testing() {
    return tray;
  }

  bool tray_initialized_for_testing() {
    return tray_initialized_state().load();
  }

  void reset_tray_data_for_testing() {
    const std::scoped_lock lock(tray_state_mutex());
    tray.icon = tray.allIconPaths[0];
    tray.tooltip = PROJECT_NAME;
    tray.notification_icon = nullptr;
    tray.notification_text = nullptr;
    tray.notification_title = nullptr;
    tray.notification_cb = nullptr;
    #ifdef _WIN32
    virtualhid_license_menu_text_storage() = {};
    virtualhid_license_menu = initial_virtualhid_license_menu();
    #endif
  }
  #endif

  #ifdef _WIN32
  /**
   * @brief Return the user-visible label for a Virtual HID Driver license state.
   *
   * @param state License state reported by libvirtualhid.
   * @return Short state label suitable for a tray menu.
   */
  std::string_view virtualhid_license_state_label(lvh::LicenseState state) {
    using enum lvh::LicenseState;

    switch (state) {
      case unlicensed:
        return "Not Activated";
      case licensed:
        return "Licensed";
      case expired:
        return "Expired";
      case disabled:
        return "Disabled";
      case invalid:
        return "Invalid";
      case unavailable:
      default:
        return "Unavailable";
    }
  }

  /**
   * @brief Return explanatory tray text for a non-active license state.
   *
   * @param state License state reported by libvirtualhid.
   * @return Short explanation of the state.
   */
  std::string_view virtualhid_license_state_detail(lvh::LicenseState state) {
    using enum lvh::LicenseState;

    switch (state) {
      case unlicensed:
        return "No license is active on this machine";
      case expired:
        return "The license on this machine has expired";
      case disabled:
        return "The license on this machine is disabled";
      case invalid:
        return "The license on this machine is invalid";
      case licensed:
        return "This machine is activated";
      case unavailable:
      default:
        return "The local license service is unavailable";
    }
  }

  /**
   * @brief Assign text and behavior to one Virtual HID Driver submenu item.
   *
   * @tparam Callback Callback type accepted by the tray library.
   * @param index Submenu index to populate.
   * @param text User-visible menu text.
   * @param disabled Whether the item is informational rather than actionable.
   * @param callback Callback invoked for actionable items.
   */
  template<typename Callback = std::nullptr_t>
  void set_virtualhid_license_menu_item(
    const std::size_t index,
    std::string text,
    const bool disabled,
    Callback callback = nullptr
  ) {
    auto &menu_text = virtualhid_license_menu_text_storage();
    menu_text[index] = std::move(text);
    virtualhid_license_menu[index] = {
      .text = menu_text[index].c_str(),
      .disabled = disabled,
      .cb = callback,
    };
  }

  /**
   * @brief Rebuild the Virtual HID Driver submenu for the latest license state.
   *
   * @param license Latest machine license details.
   */
  void rebuild_virtualhid_license_menu(const lvh::LicenseStatus &license) {
    virtualhid_license_menu = {};
    virtualhid_license_menu_text_storage() = {};

    set_virtualhid_license_menu_item(0, std::format("Status: {}", virtualhid_license_state_label(license.state)), true);
    auto separator_index = 5U;
    if (license.licensed()) {
      set_virtualhid_license_menu_item(
        1,
        license.plan_name.empty() ? std::string {virtualhid_license_state_detail(license.state)} : std::format("Plan: {}", license.plan_name),
        true
      );
      set_virtualhid_license_menu_item(
        2,
        license.customer_email.empty() ? "Customer: Not reported" : std::format("Customer: {}", license.customer_email),
        true
      );
      set_virtualhid_license_menu_item(
        3,
        license.activation_limit == 0 ?
          "Machine activations: Not reported" :
          std::format("Machine activations: {} / {}", license.activation_usage, license.activation_limit),
        true
      );
      separator_index = 4U;
      set_virtualhid_license_menu_item(5, "View License Details", false, tray_virtualhid_license_cb);
      set_virtualhid_license_menu_item(6, "Manage License", false, tray_virtualhid_license_cb);
    } else {
      set_virtualhid_license_menu_item(1, std::string {virtualhid_license_state_detail(license.state)}, true);
      set_virtualhid_license_menu_item(2, "Full virtual gamepad support is locked", true);
      set_virtualhid_license_menu_item(
        3,
        license.service_available ? "License service: Available" : "License service: Unavailable",
        true
      );
      set_virtualhid_license_menu_item(4, "Activate this machine to use Virtual HID Driver", true);
      set_virtualhid_license_menu_item(6, "Activate License", false, tray_virtualhid_license_cb);
      set_virtualhid_license_menu_item(7, "Buy License", false, tray_virtualhid_license_cb);
    }
    virtualhid_license_menu[separator_index] = {.text = "-"};
    const auto benefits_index = separator_index + 3U;
    set_virtualhid_license_menu_item(benefits_index, "Benefits over ViGEmBus", false);
    virtualhid_license_menu[benefits_index].submenu = virtualhid_benefits_menu.data();
    set_virtualhid_license_menu_item(benefits_index + 1U, "Download Virtual HID Driver", false, tray_virtualhid_download_cb);
  }

  /**
   * @brief Clear notification fields before replacing the tray state.
   */
  void clear_tray_notification() {
    tray.notification_title = nullptr;
    tray.notification_text = nullptr;
    tray.notification_cb = nullptr;
    tray.notification_icon = nullptr;
  }

  void update_tray_virtualhid_license(const lvh::LicenseStatus &license, const bool notify_if_unlicensed) {
    const std::scoped_lock lock(tray_state_mutex());
    clear_tray_notification();
    rebuild_virtualhid_license_menu(license);

    if (notify_if_unlicensed && !license.licensed()) {
      tray.notification_title = "Activate Virtual HID Driver";
      tray.notification_text =
        "Adds Xbox One/Series, DualSense (DS5), Switch Pro, and Generic gamepads beyond ViGEmBus. Actively maintained by LizardByte. Click to activate or buy a license; details remain in the tray menu.";
      tray.notification_icon = tray.allIconPaths[4];
      tray.notification_cb = []() {
        launch_ui("/troubleshooting#virtualhid-license");
      };
    }

    if (tray_initialized_state().load()) {
      tray_update(&tray);
    }
  }

  void prepare_tray_virtualhid_license() {
    const auto result = lvh::get_license_status();
    update_tray_virtualhid_license(result.license, !result.license.licensed());
  }
  #endif

  /**
   * @brief Get resource path.
   *
   * @param relativePath Relative path.
   * @return Absolute path to the resource file for the current platform bundle layout.
   */
  const char *GetResourcePath(const char *relativePath) {
    if (!relativePath || !*relativePath) {
      return nullptr;
    }

    if (std::filesystem::path {relativePath}.is_absolute()) {
      return relativePath;
    }

  #if defined(_WIN32) || defined(__APPLE__)
    // Simple cache ensures our string pointers live forever.
    static std::unordered_map<std::string, std::string, transparent_string_hash_t, std::equal_to<>> g_cache;
    auto search = g_cache.find(std::string_view {relativePath});
    if (search != g_cache.end()) {
      return search->second.c_str();
    }
  #endif

  #ifdef _WIN32
    std::array<wchar_t, 32768> executable {};
    const auto executable_length = GetModuleFileNameW(nullptr, executable.data(), executable.size());
    if (executable_length == 0 || executable_length >= executable.size()) {
      return relativePath;
    }

    const auto full_path =
      (std::filesystem::path {executable.data()}.parent_path() / utf_utils::from_utf8(relativePath)).lexically_normal();
    auto full = utf_utils::to_utf8(full_path.wstring());

    BOOST_LOG(debug) << "System Tray: using " << full << " for icon path";

    const auto it = g_cache.try_emplace(relativePath, std::move(full)).first;
    return it->second.c_str();
  #elif defined(__APPLE__)
    // If we're running from an .app bundle, get the internal Resources dir
    CFBundleRef bundle = CFBundleGetMainBundle();
    if (!bundle) {
      return relativePath;
    }

    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
    if (!resourcesURL) {
      return relativePath;
    }

    char resourcesPath[PATH_MAX];
    bool ok = CFURLGetFileSystemRepresentation(
      resourcesURL,
      true,
      reinterpret_cast<UInt8 *>(resourcesPath),
      sizeof(resourcesPath)
    );
    CFRelease(resourcesURL);
    if (!ok) {
      return relativePath;
    }

    std::string full;
    if (relativePath && relativePath[0] == '/') {
      full = relativePath;
    } else {
      full = std::string(resourcesPath) + "/" + relativePath;
    }

    BOOST_LOG(debug) << "System Tray: using " << full << " for icon path";

    const auto it = g_cache.try_emplace(relativePath, std::move(full)).first;
    return it->second.c_str();
  #else
    return relativePath;
  #endif
  }

  /**
   * @brief Resolve tray icons against the current executable or application bundle.
   */
  void resolve_tray_icon_paths() {
  #if defined(_WIN32) || defined(__APPLE__)
    std::optional<int> notification_icon_index;
    if (tray.notification_icon != nullptr) {
      for (int index = 0; index < tray.iconPathCount; ++index) {
        if (std::string_view {tray.notification_icon} == tray.allIconPaths[index]) {
          notification_icon_index = index;
          break;
        }
      }
    }

    for (int index = 0; index < tray.iconPathCount; ++index) {
      tray.allIconPaths[index] = GetResourcePath(tray.allIconPaths[index]);
    }
    tray.icon = tray.allIconPaths[0];
    if (notification_icon_index.has_value()) {
      tray.notification_icon = tray.allIconPaths[*notification_icon_index];
    }
  #endif
  }

  #ifdef SUNSHINE_TESTS
  const char *resource_path_for_testing(const char *relative_path) {
    return GetResourcePath(relative_path);
  }

  void resolve_tray_icon_paths_for_testing() {
    resolve_tray_icon_paths();
  }
  #endif

  /**
   * @brief Initialize the tray, optionally observing an owning thread's stop request.
   *
   * @param stop_token Stop token supplied by the managed tray worker, or an empty token for direct initialization.
   * @return Zero when initialization succeeds; otherwise, a non-zero error code.
   */
  int init_tray_with_stop_token(const std::stop_token stop_token) {
  #ifdef _WIN32
    // If we're running as SYSTEM, Explorer.exe will not have permission to open our thread handle
    // to monitor for thread termination. If Explorer fails to open our thread, our tray icon
    // will persist forever if we terminate unexpectedly. To avoid this, we will modify our thread
    // DACL to add an ACE that allows SYNCHRONIZE access to Everyone.
    {
      PACL old_dacl;
      PSECURITY_DESCRIPTOR sd;
      auto security_error = GetSecurityInfo(GetCurrentThread(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd);
      if (security_error != ERROR_SUCCESS) {
        BOOST_LOG(warning) << "GetSecurityInfo() failed: "sv << security_error;
        return 1;
      }

      auto free_sd = util::fail_guard([sd]() {
        LocalFree(sd);
      });

      SID_IDENTIFIER_AUTHORITY sid_authority = SECURITY_WORLD_SID_AUTHORITY;
      PSID world_sid;
      if (!AllocateAndInitializeSid(&sid_authority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &world_sid)) {
        security_error = GetLastError();
        BOOST_LOG(warning) << "AllocateAndInitializeSid() failed: "sv << security_error;
        return 1;
      }

      auto free_sid = util::fail_guard([world_sid]() {
        FreeSid(world_sid);
      });

      EXPLICIT_ACCESS ea {};
      ea.grfAccessPermissions = SYNCHRONIZE;
      ea.grfAccessMode = GRANT_ACCESS;
      ea.grfInheritance = NO_INHERITANCE;
      ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
      ea.Trustee.ptstrName = (LPSTR) world_sid;

      PACL new_dacl;
      security_error = SetEntriesInAcl(1, &ea, old_dacl, &new_dacl);
      if (security_error != ERROR_SUCCESS) {
        BOOST_LOG(warning) << "SetEntriesInAcl() failed: "sv << security_error;
        return 1;
      }

      auto free_new_dacl = util::fail_guard([new_dacl]() {
        LocalFree(new_dacl);
      });

      security_error = SetSecurityInfo(GetCurrentThread(), SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl, nullptr);
      if (security_error != ERROR_SUCCESS) {
        BOOST_LOG(warning) << "SetSecurityInfo() failed: "sv << security_error;
        return 1;
      }
    }

    // Wait for the shell to be initialized before registering the tray icon.
    // This ensures the tray icon works reliably after a logoff/logon cycle.
    while (GetShellWindow() == nullptr && !stop_token.stop_requested()) {
      Sleep(1000);
    }
    if (stop_token.stop_requested()) {
      return 1;
    }
  #endif

    resolve_tray_icon_paths();

    tray_set_log_callback(qt_log_to_boost);

    tray_set_app_info(PROJECT_NAME, PROJECT_NAME, PROJECT_FQDN);

    {
      const std::scoped_lock lock(tray_state_mutex());
      if (stop_token.stop_requested()) {
        return 1;
      }
      if (tray_init(&tray) < 0) {
        BOOST_LOG(warning) << "Failed to create system tray"sv;
        return 1;
      }

      tray_initialized_state().store(true);
    }

    BOOST_LOG(info) << "System tray created"sv;
    return 0;
  }

  int init_tray() {
    return init_tray_with_stop_token({});
  }

  int process_tray_events() {
    if (!tray_initialized_state().load()) {
      BOOST_LOG(error) << "System tray is not initialized"sv;
      return 1;
    }

    // Block until an event is processed or tray_quit() is called
    return tray_loop(1);
  }

  int end_tray() {
    auto &worker = tray_worker_thread();
    worker.request_stop();

    {
      const std::scoped_lock lock(tray_state_mutex());
      if (tray_initialized_state().exchange(false)) {
        tray_exit();
      }
    }

    if (worker.joinable() && worker.get_id() != std::this_thread::get_id()) {
      worker.join();
    }
    return 0;
  }

  void update_tray_playing(std::string app_name) {
    const std::scoped_lock lock(tray_state_mutex());
    if (!tray_initialized_state().load()) {
      return;
    }

    tray.notification_title = nullptr;
    tray.notification_text = nullptr;
    tray.notification_cb = nullptr;
    tray.notification_icon = nullptr;
    tray.icon = tray.allIconPaths[2];
    tray_update(&tray);
    tray.icon = tray.allIconPaths[2];
    tray.notification_title = "Stream Started";

    static std::string msg = std::format("Streaming started for {}", app_name);
    tray.notification_text = msg.c_str();
    tray.tooltip = msg.c_str();
    tray.notification_icon = tray.allIconPaths[2];
    tray_update(&tray);
  }

  void update_tray_pausing(std::string app_name) {
    const std::scoped_lock lock(tray_state_mutex());
    if (!tray_initialized_state().load()) {
      return;
    }

    tray.notification_title = nullptr;
    tray.notification_text = nullptr;
    tray.notification_cb = nullptr;
    tray.notification_icon = nullptr;
    tray.icon = tray.allIconPaths[3];
    tray_update(&tray);

    static std::string msg = std::format("Streaming paused for {}", app_name);
    tray.icon = tray.allIconPaths[3];
    tray.notification_title = "Stream Paused";
    tray.notification_text = msg.c_str();
    tray.tooltip = msg.c_str();
    tray.notification_icon = tray.allIconPaths[3];
    tray_update(&tray);
  }

  void update_tray_stopped(std::string app_name) {
    const std::scoped_lock lock(tray_state_mutex());
    if (!tray_initialized_state().load()) {
      return;
    }

    tray.notification_title = nullptr;
    tray.notification_text = nullptr;
    tray.notification_cb = nullptr;
    tray.notification_icon = nullptr;
    tray.icon = tray.allIconPaths[0];
    tray_update(&tray);

    static std::string msg = std::format("Application {} successfully stopped", app_name);
    tray.icon = tray.allIconPaths[0];
    tray.notification_icon = tray.allIconPaths[0];
    tray.notification_title = "Application Stopped";
    tray.notification_text = msg.c_str();
    tray.tooltip = PROJECT_NAME;
    tray_update(&tray);
  }

  void update_tray_require_pin() {
    const std::scoped_lock lock(tray_state_mutex());
    if (!tray_initialized_state().load()) {
      return;
    }

    tray.notification_title = nullptr;
    tray.notification_text = nullptr;
    tray.notification_cb = nullptr;
    tray.notification_icon = nullptr;
    tray.icon = tray.allIconPaths[0];
    tray_update(&tray);
    tray.icon = tray.allIconPaths[0];
    tray.notification_title = "Incoming Pairing Request";
    tray.notification_text = "Click here to complete the pairing process";
    tray.notification_icon = tray.allIconPaths[1];
    tray.tooltip = PROJECT_NAME;
    tray.notification_cb = []() {
      launch_ui("/pin");
    };
    tray_update(&tray);
  }

  // Threading functions available on all platforms
  /**
   * @brief Run the managed system tray event loop.
   *
   * @param stop_token Stop token owned by the tray worker thread.
   */
  static void tray_thread_worker(const std::stop_token stop_token) {
    platf::set_thread_name("system_tray");
    BOOST_LOG(info) << "System tray thread started"sv;

    // Initialize the tray in this thread
    if (init_tray_with_stop_token(stop_token) != 0) {
      if (!stop_token.stop_requested()) {
        BOOST_LOG(error) << "Failed to initialize tray in thread"sv;
      }
      return;
    }

    // Main tray event loop
    while (!stop_token.stop_requested() && process_tray_events() == 0) {
      // Continue until the tray requests shutdown or event processing stops.
    }

    {
      const std::scoped_lock lock(tray_state_mutex());
      tray_initialized_state().store(false);
    }

    BOOST_LOG(info) << "System tray thread ended"sv;
  }

  int init_tray_threaded() {
    try {
      auto &worker = tray_worker_thread();
      if (worker.joinable()) {
        BOOST_LOG(error) << "System tray thread is already running"sv;
        return 1;
      }
      worker = std::jthread(tray_thread_worker);

      BOOST_LOG(info) << "System tray thread initialized successfully"sv;
      return 0;
    } catch (const std::system_error &e) {
      BOOST_LOG(error) << "Failed to create tray thread: " << e.what();
      return 1;
    }
  }

}  // namespace system_tray
#endif
