#include <softbus_monitor/HeartbeatMonitor.h>

#include <softbus_core/logging/Logger.h>
#include <softbus_core/time/HwClock.h>

#include <vector>

namespace softbus::monitor {

HeartbeatMonitor::HeartbeatMonitor(std::chrono::milliseconds timeout)
    : timeout_(timeout)
{
}

void HeartbeatMonitor::attachBus(softbus::bus::Bus* bus)
{
    bus_ = bus;
    if (!bus_) {
        return;
    }
    hbSub_ = bus_->subscribe("system/heartbeat/#", [this](const softbus::bus::BusMessage& msg) {
        onHeartbeat(msg);
    });
}

void HeartbeatMonitor::onHeartbeat(const softbus::bus::BusMessage& message)
{
    if (message.deviceId.empty()) {
        return;
    }
    std::lock_guard<std::mutex> lock(mutex_);
    lastSeen_[message.deviceId] = std::chrono::steady_clock::now();
}

void HeartbeatMonitor::tick()
{
    const auto now = std::chrono::steady_clock::now();
    std::vector<std::string> offline;
    {
        std::lock_guard<std::mutex> lock(mutex_);
        for (const auto& [deviceId, last] : lastSeen_) {
            if (now - last > timeout_) {
                offline.push_back(deviceId);
            }
        }
    }
    for (const auto& deviceId : offline) {
        publishOfflineEvent(deviceId);
    }
}

void HeartbeatMonitor::publishOfflineEvent(const std::string& deviceId)
{
    if (!bus_) {
        return;
    }
    softbus::core::SoftbusEvent ev;
    ev.deviceId = deviceId;
    ev.eventType = "edge_offline";
    ev.message = "heartbeat timeout";
    ev.timestampNs = softbus::core::HwClock::nowNs();

    softbus::bus::BusMessage msg;
    msg.type = softbus::bus::BusPayloadType::Event;
    msg.event = ev;
    bus_->publish("event/" + deviceId + "/edge_offline", msg);
    SB_LOG_WARN("HeartbeatMonitor", "edge offline: ", deviceId);
}

} // namespace softbus::monitor
