#include "core/metadata/MetadataRegistry.h"

#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonParseError>

namespace softbus::core::metadata
{

namespace
{

using softbus::core::models::DataDomain;
using softbus::core::models::DataType;

DataDomain parseDomain(const QString& s)
{
    const QString x = s.trimmed().toUpper();
    if (x == QStringLiteral("PROCESS_PARAM")) {
        return DataDomain::PROCESS_PARAM;
    }
    if (x == QStringLiteral("CONTROL_CMD")) {
        return DataDomain::CONTROL_CMD;
    }
    if (x == QStringLiteral("MONITOR_DATA")) {
        return DataDomain::MONITOR_DATA;
    }
    if (x == QStringLiteral("HEALTH_DIAG")) {
        return DataDomain::HEALTH_DIAG;
    }
    if (x == QStringLiteral("CONFIG_PARAM")) {
        return DataDomain::CONFIG_PARAM;
    }
    return DataDomain::UNKNOWN;
}

DataType parseDataType(const QString& s)
{
    const QString x = s.trimmed().toUpper();
    if (x == QStringLiteral("BOOL")) {
        return DataType::BOOL;
    }
    if (x == QStringLiteral("INT32")) {
        return DataType::INT32;
    }
    if (x == QStringLiteral("UINT32")) {
        return DataType::UINT32;
    }
    if (x == QStringLiteral("INT64")) {
        return DataType::INT64;
    }
    if (x == QStringLiteral("FLOAT32")) {
        return DataType::FLOAT32;
    }
    if (x == QStringLiteral("FLOAT64")) {
        return DataType::FLOAT64;
    }
    if (x == QStringLiteral("STRING")) {
        return DataType::STRING;
    }
    if (x == QStringLiteral("BYTE_ARRAY")) {
        return DataType::BYTE_ARRAY;
    }
    return DataType::FLOAT64;
}

} // namespace

bool MetadataRegistry::loadFromFile(const QString& filePath)
{
    m_lastError.clear();
    m_defsById.clear();

    QFile f(filePath);
    if (!f.open(QIODevice::ReadOnly)) {
        m_lastError = QStringLiteral("failed to open metadata file: %1").arg(filePath);
        return false;
    }

    QJsonParseError pe;
    const auto      doc = QJsonDocument::fromJson(f.readAll(), &pe);
    if (pe.error != QJsonParseError::NoError || !doc.isObject()) {
        m_lastError = QStringLiteral("invalid metadata json: %1").arg(pe.errorString());
        return false;
    }

    const auto root = doc.object();
    const auto arr  = root.value(QStringLiteral("metadatas")).toArray();
    for (const auto& item : arr) {
        if (!item.isObject()) {
            continue;
        }
        const auto one = parseOne(item.toObject());
        if (!one.has_value() || one->metadataId.isEmpty()) {
            continue;
        }
        m_defsById.insert(one->metadataId, *one);
    }

    if (m_defsById.isEmpty()) {
        m_lastError = QStringLiteral("metadata file contains no valid entries");
        return false;
    }
    return true;
}

const softbus::core::models::MetadataDef* MetadataRegistry::findById(const QString& metadataId) const
{
    const auto it = m_defsById.constFind(metadataId);
    if (it == m_defsById.constEnd()) {
        return nullptr;
    }
    return &it.value();
}

std::optional<softbus::core::models::MetadataDef> MetadataRegistry::parseOne(const QJsonObject& o)
{
    softbus::core::models::MetadataDef def;
    def.metadataId = o.value(QStringLiteral("metadataId")).toString();
    def.name       = o.value(QStringLiteral("name")).toString();
    def.description = o.value(QStringLiteral("description")).toString();
    def.domain      = parseDomain(o.value(QStringLiteral("domain")).toString());
    def.semanticType = parseDataType(o.value(QStringLiteral("semanticType")).toString());
    def.unit        = o.value(QStringLiteral("unit")).toString();
    def.minValue    = o.value(QStringLiteral("minValue")).toDouble(0.0);
    def.maxValue    = o.value(QStringLiteral("maxValue")).toDouble(0.0);
    def.deadband    = o.value(QStringLiteral("deadband")).toDouble(0.0);
    def.isWriteable = o.value(QStringLiteral("isWriteable")).toBool(false);
    return def;
}

} // namespace softbus::core::metadata
