/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "network-util.h"
#include "networkd-json.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
#include "sort-util.h"

static int network_build_json(Network *network, JsonVariant **ret) {
        assert(network);
        assert(ret);

        return json_build(ret, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("NetworkFile", JSON_BUILD_STRING(network->filename))));
}

static int device_build_json(sd_device *device, JsonVariant **ret) {
        const char *link = NULL, *path = NULL, *vendor = NULL, *model = NULL;

        assert(device);
        assert(ret);

        (void) sd_device_get_property_value(device, "ID_NET_LINK_FILE", &link);
        (void) sd_device_get_property_value(device, "ID_PATH", &path);

        if (sd_device_get_property_value(device, "ID_VENDOR_FROM_DATABASE", &vendor) < 0)
                (void) sd_device_get_property_value(device, "ID_VENDOR", &vendor);

        if (sd_device_get_property_value(device, "ID_MODEL_FROM_DATABASE", &model) < 0)
                (void) sd_device_get_property_value(device, "ID_MODEL", &model);

        return json_build(ret, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("LinkFile", JSON_BUILD_STRING(link)),
                                        JSON_BUILD_PAIR("Path", JSON_BUILD_STRING(path)),
                                        JSON_BUILD_PAIR("Vendor", JSON_BUILD_STRING(vendor)),
                                        JSON_BUILD_PAIR("Model", JSON_BUILD_STRING(model))));
}

int link_build_json(Link *link, JsonVariant **ret) {
        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
        _cleanup_free_ char *type = NULL;
        int r;

        assert(link);
        assert(ret);

        r = link_get_type_string(link->sd_device, link->iftype, &type);
        if (r == -ENOMEM)
                return r;

        r = json_build(&v, JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR("Index", JSON_BUILD_INTEGER(link->ifindex)),
                                        JSON_BUILD_PAIR("Name", JSON_BUILD_STRING(link->ifname)),
                                        JSON_BUILD_PAIR("AlternativeNames", JSON_BUILD_STRV(link->alternative_names)),
                                        JSON_BUILD_PAIR("Type", JSON_BUILD_STRING(type)),
                                        JSON_BUILD_PAIR("Driver", JSON_BUILD_STRING(link->driver)),
                                        JSON_BUILD_PAIR("SetupState", JSON_BUILD_STRING(link_state_to_string(link->state))),
                                        JSON_BUILD_PAIR("OperationalState", JSON_BUILD_STRING(link_operstate_to_string(link->operstate))),
                                        JSON_BUILD_PAIR("CarrierState", JSON_BUILD_STRING(link_carrier_state_to_string(link->carrier_state))),
                                        JSON_BUILD_PAIR("AddressState", JSON_BUILD_STRING(link_address_state_to_string(link->address_state))),
                                        JSON_BUILD_PAIR("IPv4AddressState", JSON_BUILD_STRING(link_address_state_to_string(link->ipv4_address_state))),
                                        JSON_BUILD_PAIR("IPv6AddressState", JSON_BUILD_STRING(link_address_state_to_string(link->ipv6_address_state))),
                                        JSON_BUILD_PAIR("OnlineState", JSON_BUILD_STRING(link_online_state_to_string(link->online_state)))));
        if (r < 0)
                return r;

        if (link->network) {
                _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;

                r = network_build_json(link->network, &w);
                if (r < 0)
                        return r;

                r = json_variant_merge(&v, w);
                if (r < 0)
                        return r;
        }

        if (link->sd_device) {
                _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;

                r = device_build_json(link->sd_device, &w);
                if (r < 0)
                        return r;

                r = json_variant_merge(&v, w);
                if (r < 0)
                        return r;
        }

        *ret = TAKE_PTR(v);
        return 0;
}

static int link_json_compare(JsonVariant * const *a, JsonVariant * const *b) {
        intmax_t index_a, index_b;

        assert(a && *a);
        assert(b && *b);

        index_a = json_variant_integer(json_variant_by_key(*a, "Index"));
        index_b = json_variant_integer(json_variant_by_key(*b, "Index"));

        return CMP(index_a, index_b);
}

int manager_build_json(Manager *manager, JsonVariant **ret) {
        JsonVariant **elements;
        Link *link;
        size_t n = 0;
        int r;

        assert(manager);
        assert(ret);

        elements = new(JsonVariant*, hashmap_size(manager->links_by_index));
        if (!elements)
                return -ENOMEM;

        HASHMAP_FOREACH(link, manager->links_by_index) {
                r = link_build_json(link, elements + n);
                if (r < 0)
                        goto finalize;
                n++;
        }

        typesafe_qsort(elements, n, link_json_compare);

        r = json_build(ret, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("Interfaces", JSON_BUILD_VARIANT_ARRAY(elements, n))));

finalize:
        json_variant_unref_many(elements, n);
        free(elements);
        return r;
}
