#include "tray_bridge_model.h"

#include <QJsonDocument>
#include <QJsonValue>
#include <QMap>

namespace {
bool looksLikeSnapshot(const QJsonObject &object)
{
    return object.contains(QStringLiteral("backendState")) || object.contains(QStringLiteral("self")) ||
           object.contains(QStringLiteral("prefs")) || object.contains(QStringLiteral("peers")) ||
           object.contains(QStringLiteral("exitNodes"));
}

QJsonObject extractSnapshotObject(const QJsonObject &object)
{
    if (looksLikeSnapshot(object)) {
        return object;
    }

    static const char *const keys[] = {"snapshot", "data", "payload"};
    for (const char *key : keys) {
        const QJsonValue value = object.value(QLatin1String(key));
        if (!value.isObject()) {
            continue;
        }
        const QJsonObject candidate = value.toObject();
        if (looksLikeSnapshot(candidate)) {
            return candidate;
        }
    }
    return {};
}

QString extractErrorMessage(const QJsonObject &object)
{
    const QString explicitError = object.value(QStringLiteral("error")).toString().trimmed();
    if (!explicitError.isEmpty()) {
        return explicitError;
    }

    const QString type = object.value(QStringLiteral("type")).toString().trimmed().toLower();
    const QString event = object.value(QStringLiteral("event")).toString().trimmed().toLower();
    if (type == QStringLiteral("error") || type == QStringLiteral("fatal") || event == QStringLiteral("error") ||
        event == QStringLiteral("fatal")) {
        return object.value(QStringLiteral("message")).toString().trimmed();
    }
    return {};
}

QString sectionTitleForPeer(const QJsonObject &peer, const QString &selfAccountLabel)
{
    if (peer.value(QStringLiteral("tagged")).toBool()) {
        return QStringLiteral("标签节点");
    }

    const QString ownerLabel = peer.value(QStringLiteral("ownerLabel")).toString().trimmed();
    const QString userLabel = peer.value(QStringLiteral("userLabel")).toString().trimmed();
    const QString accountLabel = ownerLabel.isEmpty() ? userLabel : ownerLabel;
    if (!selfAccountLabel.isEmpty() && accountLabel == selfAccountLabel) {
        return QStringLiteral("我的其他设备");
    }
    if (!accountLabel.isEmpty()) {
        return accountLabel;
    }
    return QStringLiteral("其他设备");
}
}

BridgeStreamEvent parseBridgeStreamLine(const QByteArray &line)
{
    const QByteArray trimmed = line.trimmed();
    if (trimmed.isEmpty()) {
        return {BridgeStreamEvent::Invalid, {}, {}, {}, {}};
    }

    QJsonParseError parseError;
    const QJsonDocument document = QJsonDocument::fromJson(trimmed, &parseError);
    if (parseError.error != QJsonParseError::NoError || !document.isObject()) {
        return {BridgeStreamEvent::Invalid, {}, {}, {}, {}};
    }

    const QJsonObject object = document.object();
    const QJsonObject snapshot = extractSnapshotObject(object);
    if (!snapshot.isEmpty()) {
        return {BridgeStreamEvent::Snapshot, snapshot, {}, {}, {}};
    }

    const QString errorMessage = extractErrorMessage(object);
    if (!errorMessage.isEmpty()) {
        return {BridgeStreamEvent::Error, {}, {}, errorMessage, {}};
    }

    const QString type = object.value(QStringLiteral("type")).toString().trimmed().toLower();
    if (type == QStringLiteral("state")) {
        return {BridgeStreamEvent::State,
                {},
                object,
                {},
                object.value(QStringLiteral("state")).toString()};
    }
    if (type == QStringLiteral("prefs")) {
        return {BridgeStreamEvent::Prefs, {}, object.value(QStringLiteral("prefs")).toObject(), {}, {}};
    }
    if (type == QStringLiteral("netmap")) {
        return {BridgeStreamEvent::Netmap, {}, object.value(QStringLiteral("netmap")).toObject(), {}, {}};
    }
    if (type == QStringLiteral("version")) {
        return {BridgeStreamEvent::Version,
                {},
                {},
                {},
                object.value(QStringLiteral("version")).toString()};
    }
    if (type == QStringLiteral("browse-url")) {
        return {BridgeStreamEvent::BrowseURL, {}, {}, {}, object.value(QStringLiteral("url")).toString()};
    }

    const QString event = object.value(QStringLiteral("event")).toString().trimmed().toLower();
    if (type == QStringLiteral("keepalive") || event == QStringLiteral("keepalive") || event == QStringLiteral("ping")) {
        return {BridgeStreamEvent::KeepAlive, {}, {}, {}, {}};
    }

    return {BridgeStreamEvent::Invalid, {}, {}, {}, {}};
}

QList<PeerMenuSection> buildPeerMenuSections(const QJsonObject &snapshot)
{
    QList<QJsonObject> myDevices;
    QList<QJsonObject> tagged;
    QMap<QString, QList<QJsonObject>> ownerGroups;
    const QString selfAccountLabel = snapshot.value(QStringLiteral("accountLabel")).toString().trimmed();
    const QJsonArray peers = snapshot.value(QStringLiteral("peers")).toArray();

    for (const QJsonValue &value : peers) {
        if (!value.isObject()) {
            continue;
        }
        const QJsonObject peer = value.toObject();
        const QString sectionTitle = sectionTitleForPeer(peer, selfAccountLabel);
        if (sectionTitle == QStringLiteral("我的其他设备")) {
            myDevices.append(peer);
            continue;
        }
        if (sectionTitle == QStringLiteral("标签节点")) {
            tagged.append(peer);
            continue;
        }
        ownerGroups[sectionTitle].append(peer);
    }

    auto appendSection = [](QList<PeerMenuSection> *sections, const QString &title, const QList<QJsonObject> &items) {
        if (items.isEmpty()) {
            return;
        }
        PeerMenuSection section;
        section.title = title;
        for (const QJsonObject &item : items) {
            section.peers.append(item);
        }
        sections->append(section);
    };

    QList<PeerMenuSection> sections;
    appendSection(&sections, QStringLiteral("我的其他设备"), myDevices);
    for (auto it = ownerGroups.cbegin(); it != ownerGroups.cend(); ++it) {
        appendSection(&sections, it.key(), it.value());
    }
    appendSection(&sections, QStringLiteral("标签节点"), tagged);
    return sections;
}

QString peerMenuLabel(const QJsonObject &peer)
{
    QString label = peer.value(QStringLiteral("name")).toString(QStringLiteral("-"));
    const QString ip = peer.value(QStringLiteral("ip")).toString().trimmed();
    if (!ip.isEmpty()) {
        label += QStringLiteral(" (") + ip + QStringLiteral(")");
    }
    if (peer.value(QStringLiteral("online")).toBool()) {
        label += QStringLiteral(" [在线]");
    }
    return label;
}

QString exitNodeMenuLabel(const QJsonObject &exitNode)
{
    QString label = exitNode.value(QStringLiteral("name")).toString(QStringLiteral("不使用"));
    const QString ip = exitNode.value(QStringLiteral("ip")).toString().trimmed();
    if (!ip.isEmpty()) {
        label += QStringLiteral(" (") + ip + QStringLiteral(")");
    }
    return label;
}
