#include <softbus_opcua/UaModelBinder.h>

#include <softbus_core/logging/Logger.h>

#if SOFTBUS_HAS_OPCUA
#include <open62541/server.h>
#endif

namespace softbus::opcua {

UaModelBinder::UaModelBinder(UA_Server* server)
    : server_(server)
{
}

void UaModelBinder::attachBus(softbus::bus::Bus* bus)
{
    bus_ = bus;
    if (!bus_) {
        return;
    }
    dataSub_ = bus_->subscribe("data/#", [this](const softbus::bus::BusMessage& msg) {
        onBusMessage(msg);
    });
}

void UaModelBinder::onBusMessage(const softbus::bus::BusMessage& message)
{
    if (message.type != softbus::bus::BusPayloadType::DataPoint) {
        return;
    }
    bindValue(message.dataPoint.objectRef, message.dataPoint.value);
}

void UaModelBinder::registerPointNode(const std::string& objectRef, const UA_NodeId& nodeId)
{
#if SOFTBUS_HAS_OPCUA
    UA_NodeId copy{};
    UA_NodeId_copy(&nodeId, &copy);
    pointNodes_[objectRef] = copy;
#else
    (void)objectRef;
    (void)nodeId;
#endif
}

bool UaModelBinder::bind(const softbus::core::DOMMessage& message)
{
    const std::string key = message.id.empty() ? message.sourceId : message.id;
    double numericValue = 0.0;
    if (message.value.contains("value")) {
        numericValue = message.value["value"].get<double>();
    } else if (message.value.contains("registerValue")) {
        numericValue = message.value["registerValue"].get<double>();
    }
    return bindValue(key, numericValue, message.traceId);
}

bool UaModelBinder::bindValue(const std::string& objectRef, double value, const std::string& traceId)
{
#if !SOFTBUS_HAS_OPCUA
    (void)objectRef;
    (void)value;
    (void)traceId;
    return false;
#else
    if (!server_) {
        return false;
    }

    const auto it = pointNodes_.find(objectRef);
    if (it == pointNodes_.end()) {
        SB_LOG_WARN_TRACE("UaModelBinder", traceId, "No OPC UA node registered for ", objectRef);
        return false;
    }

    UA_Variant variant;
    UA_Variant_init(&variant);
    UA_Variant_setScalarCopy(&variant, &value, &UA_TYPES[UA_TYPES_DOUBLE]);
    const UA_StatusCode status = UA_Server_writeValue(server_, it->second, variant);
    UA_Variant_clear(&variant);

    if (status == UA_STATUSCODE_GOOD) {
        SB_LOG_INFO_TRACE("UaModelBinder", traceId,
            "Bound value to OPC UA node ", objectRef, " value=", value);
        return true;
    }

    SB_LOG_ERROR_TRACE("UaModelBinder", traceId, "Failed to write OPC UA node ", objectRef);
    return false;
#endif
}

} // namespace softbus::opcua
