#include "device_bus/registry/DeviceRegistry.h"

#include <QCryptographicHash>
#include <QDateTime>

#include "utils/logs/logging.h"

namespace softbus::device_bus::registry
{

DeviceRegistry::DeviceRegistry(std::shared_ptr<DeviceTreeModel> treeModel)
    : m_treeModel(std::move(treeModel))
{
}

void DeviceRegistry::registerDevice(const softbus::devices::DeviceInfo& info)
{
    m_devicesById.insert(info.id, info);
}

void DeviceRegistry::unregisterDevice(int id)
{
    m_devicesById.remove(id);
}

std::optional<softbus::devices::DeviceInfo> DeviceRegistry::findById(int id) const
{
    auto it = m_devicesById.constFind(id);
    if (it == m_devicesById.constEnd()) {
        return std::nullopt;
    }
    return it.value();
}

std::vector<softbus::devices::DeviceInfo> DeviceRegistry::listDevices() const
{
    std::vector<softbus::devices::DeviceInfo> out;
    out.reserve(m_devicesById.size());
    for (auto it = m_devicesById.cbegin(); it != m_devicesById.cend(); ++it) {
        out.push_back(it.value());
    }
    return out;
}

bool DeviceRegistry::upsertFromMonitorEvent(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    if (!m_treeModel) return false;

    const QString stableKey = buildStableKey(ev);
    if (stableKey.isEmpty()) {
        return false;
    }
    const QString preferredId = buildPreferredNodeId(ev, stableKey);
    const QString now = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);

    const QString nodeId = m_treeModel->upsertNodeByStableKey(
        stableKey,
        preferredId,
        inferType(ev),
        inferEndpoint(ev),
        inferName(ev),
        buildParams(ev),
        true,
        now);

    if (!nodeId.isEmpty()) {
        m_stableKeyToNodeId.insert(stableKey, nodeId);
        return true;
    }
    return false;
}

bool DeviceRegistry::markOfflineFromMonitorEvent(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    if (!m_treeModel) return false;
    const QString stableKey = buildStableKey(ev);
    if (stableKey.isEmpty()) {
        return false;
    }
    const QString now = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
    return m_treeModel->markOfflineByStableKey(stableKey, now);
}

QString DeviceRegistry::buildStableKey(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    const QString type = inferType(ev);

    // Important: ttyACM/ttyUSB 可能“同一条物理设备多路口”但 serial 相同，
    // 旧逻辑会导致 stableKey 冲突并相互覆盖 endpoint。
    // tty 事件：devnode 必须用于唯一标识（ttyACM 可能多路口但 serial 可能相同）。
    // 若具备 SYSPATH，也一并拼接，便于跨层级归并与排查。
    if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
        ev.subsystem == QStringLiteral("tty")) {
        const QString syspath = ev.props.value(QStringLiteral("SYSPATH"));
        if (!ev.devnode.isEmpty()) {
            if (!syspath.isEmpty()) {
                return type + QStringLiteral("|syspath|") + syspath + QStringLiteral("|devnode|") + ev.devnode;
            }
            return type + QStringLiteral("|devnode|") + ev.devnode;
        }
        if (!syspath.isEmpty()) {
            return type + QStringLiteral("|syspath|") + syspath;
        }
        if (!ev.serial.isEmpty()) {
            return type + QStringLiteral("|serial|") + ev.serial;
        }
    }

    if (!ev.serial.isEmpty()) {
        return type + QStringLiteral("|serial|") + ev.serial;
    }

    if (ev.type == softbus::device_bus::monitor::DeviceEventType::NetInterface) {
        if (!ev.ifname.isEmpty()) return type + QStringLiteral("|ifname|") + ev.ifname;
        if (ev.ifindex >= 0) return type + QStringLiteral("|ifindex|") + QString::number(ev.ifindex);
    }

    const QString path = !ev.props.value(QStringLiteral("SYSPATH")).isEmpty()
                         ? ev.props.value(QStringLiteral("SYSPATH"))
                         : (!ev.devicePath.isEmpty() ? ev.devicePath : ev.devnode);
    if (!ev.vid.isEmpty() || !ev.pid.isEmpty() || !path.isEmpty()) {
        return type + QStringLiteral("|vpidpath|") + ev.vid + QStringLiteral(":") + ev.pid + QStringLiteral("|") + path;
    }

    if (!ev.devnode.isEmpty()) {
        return type + QStringLiteral("|devnode|") + ev.devnode;
    }
    return {};
}

QString DeviceRegistry::buildPreferredNodeId(const softbus::device_bus::monitor::DeviceEvent& ev, const QString& stableKey)
{
    // For tty devices (ttyS/ttyUSB/ttyACM...), a single physical device may expose
    // multiple ports that share the same serial. If we only use `serial` as id,
    // upsert() will collide by preferredId and overwrite endpoints.
    if (ev.type == softbus::device_bus::monitor::DeviceEventType::Serial ||
        ev.subsystem == QStringLiteral("tty")) {
        const QByteArray hash = QCryptographicHash::hash(stableKey.toUtf8(), QCryptographicHash::Sha1).toHex();
        return QStringLiteral("dev_") + QString::fromUtf8(hash.left(12));
    }

    if (!ev.serial.isEmpty()) {
        return QStringLiteral("dev_serial_") + ev.serial;
    }

    const QByteArray hash = QCryptographicHash::hash(stableKey.toUtf8(), QCryptographicHash::Sha1).toHex();
    return QStringLiteral("dev_") + QString::fromUtf8(hash.left(12));
}

QString DeviceRegistry::inferType(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    using softbus::device_bus::monitor::DeviceEventType;
    switch (ev.type) {
    case DeviceEventType::Serial: return QStringLiteral("serial");
    case DeviceEventType::NetInterface: return QStringLiteral("network");
    case DeviceEventType::Storage: return QStringLiteral("storage");
    case DeviceEventType::Usb: return QStringLiteral("usb");
    default: return QStringLiteral("unknown");
    }
}

QString DeviceRegistry::inferEndpoint(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    if (!ev.devnode.isEmpty()) return ev.devnode;
    if (!ev.ifname.isEmpty()) return ev.ifname;
    if (!ev.devicePath.isEmpty()) return ev.devicePath;
    return ev.props.value(QStringLiteral("SYSPATH"));
}

QString DeviceRegistry::inferName(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    if (!ev.model.isEmpty()) return ev.model;
    if (!ev.ifname.isEmpty()) return ev.ifname;
    if (!ev.devnode.isEmpty()) return ev.devnode;
    return inferType(ev);
}

QJsonObject DeviceRegistry::buildParams(const softbus::device_bus::monitor::DeviceEvent& ev)
{
    QJsonObject p;
    p.insert(QStringLiteral("online"), true);
    if (!ev.vid.isEmpty()) p.insert(QStringLiteral("vid"), ev.vid);
    if (!ev.pid.isEmpty()) p.insert(QStringLiteral("pid"), ev.pid);
    if (!ev.serial.isEmpty()) p.insert(QStringLiteral("serial"), ev.serial);
    if (!ev.vendor.isEmpty()) p.insert(QStringLiteral("vendor"), ev.vendor);
    if (!ev.model.isEmpty()) p.insert(QStringLiteral("model"), ev.model);
    if (!ev.ifname.isEmpty()) p.insert(QStringLiteral("ifname"), ev.ifname);
    if (ev.ifindex >= 0) p.insert(QStringLiteral("ifindex"), ev.ifindex);
    if (!ev.subsystem.isEmpty()) p.insert(QStringLiteral("subsystem"), ev.subsystem);
    if (!ev.devicePath.isEmpty()) p.insert(QStringLiteral("devicePath"), ev.devicePath);
    if (!ev.devnode.isEmpty()) p.insert(QStringLiteral("devnode"), ev.devnode);
    return p;
}

} // namespace softbus::device_bus::registry

