#include <softbus_bus/Bus.h>

#include <algorithm>

namespace softbus::bus {

namespace {

std::vector<std::string> splitTopic(const std::string& topic)
{
    std::vector<std::string> parts;
    std::string current;
    for (char c : topic) {
        if (c == '/') {
            if (!current.empty()) {
                parts.push_back(current);
                current.clear();
            }
        } else {
            current.push_back(c);
        }
    }
    if (!current.empty()) {
        parts.push_back(current);
    }
    return parts;
}

} // namespace

Bus& Bus::instance()
{
    static Bus bus;
    return bus;
}

Bus::SubscriptionId Bus::subscribe(const std::string& topicPattern, BusHandler handler)
{
    std::lock_guard<std::mutex> lock(mutex_);
    const SubscriptionId id = nextId_++;
    subscriptions_.push_back(Subscription{id, topicPattern, std::move(handler)});
    return id;
}

void Bus::unsubscribe(SubscriptionId id)
{
    std::lock_guard<std::mutex> lock(mutex_);
    subscriptions_.erase(
        std::remove_if(subscriptions_.begin(), subscriptions_.end(),
            [id](const Subscription& s) { return s.id == id; }),
        subscriptions_.end());
}

void Bus::publish(const std::string& topic, const BusMessage& message)
{
    std::vector<BusHandler> handlers;
    {
        std::lock_guard<std::mutex> lock(mutex_);
        for (const auto& sub : subscriptions_) {
            if (topicMatches(sub.pattern, topic)) {
                handlers.push_back(sub.handler);
            }
        }
    }
    BusMessage msg = message;
    msg.topic = topic;
    for (const auto& handler : handlers) {
        handler(msg);
    }
}

bool Bus::topicMatches(const std::string& pattern, const std::string& topic)
{
    if (pattern == topic) {
        return true;
    }
    if (pattern == "#" || pattern == "*") {
        return true;
    }

    const auto patternParts = splitTopic(pattern);
    const auto topicParts = splitTopic(topic);
    std::size_t pi = 0;
    std::size_t ti = 0;
    while (pi < patternParts.size() && ti < topicParts.size()) {
        const auto& p = patternParts[pi];
        if (p == "#") {
            return true;
        }
        if (p == "*" || p == "+") {
            ++pi;
            ++ti;
            continue;
        }
        if (p != topicParts[ti]) {
            return false;
        }
        ++pi;
        ++ti;
    }
    if (pi == patternParts.size() && ti == topicParts.size()) {
        return true;
    }
    return pi < patternParts.size() && patternParts[pi] == "#";
}

} // namespace softbus::bus
