/**
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "datasystem/common/rdma/rdma_util.h"

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/if.h>

#include "datasystem/common/log/log.h"
#include "datasystem/common/util/gflag/common_gflags.h"
#include "datasystem/common/util/status_helper.h"

namespace datasystem {
static constexpr int RDMA_LOG_LEVEL = 3;
const int OCTET = 8;
const int IP_NUM_OCTETS = 4;
const int IPV4_MAX_LENGTH = 16;
Status GetDevNameFromDestIp(const std::string &ipAddr, std::string &devName)
{
    std::istringstream iss(ipAddr);
    std::string nextToken;
    int num[4];
    for (int i = 0; std::getline(iss, nextToken, '.'); i++) {
        num[i] = atoi(nextToken.c_str());
    }

    unsigned long ip = 0;
    for (int i = 0; i < IP_NUM_OCTETS; i++) {
        ip += num[i] << (OCTET * i);
    }
    VLOG(RDMA_LOG_LEVEL) << FormatString("%lu\n", ip);

    char devname[64];
    unsigned long d, g, m;
    int r, flgs, ref, use, metric, mtu, win, ir;

    std::string path("/proc/net/route");
    FILE *fp = fopen(path.c_str(), "r");
    RETURN_RUNTIME_ERROR_IF_NULL(fp);

    /* Skip the first line. */
    r = fscanf_s(fp, "%*[^\n]\n");
    /* Empty line, read error, or EOF. Yes, if routing table
     * is completely empty, /proc/net/route has no header.
     */
    CHECK_FAIL_RETURN_STATUS(r >= 0, K_RUNTIME_ERROR, "Empty line, read error, or EOF");
    bool found = false;
    while (!found) {
        r = fscanf_s(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", devname, sizeof(devname), &d, &g, &flgs, &ref, &use, &metric,
                     &m, &mtu, &win, &ir);
        // Linux error code 11 is EAGAIN
        if (r != EAGAIN) {
            if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
                break;
            }
        }
        if (!(flgs & 0x0001)) { /* Skip interfaces that are down. */
            continue;
        }
        VLOG(RDMA_LOG_LEVEL) << FormatString("devname=%s,dest=%lld,gw=%lld,mask=%lld\n", devname, d, g, m);
        // Record device name in case the exact matching one is missing
        devName = std::string(devname);
        if ((d & m) == (ip & m) && (d & m) != 0) {
            VLOG(RDMA_LOG_LEVEL) << FormatString("find success devname=%s\n", devname);
            found = true;
        }
    }
    if (!found) {
        LOG(WARNING) << FormatString("Device name of %s is not found. Will use device name: %s", ipAddr, devName);
    }
    (void)fclose(fp);
    return Status::OK();
}

void *GetInterfaceInAddr(struct ifaddrs *ifa)
{
    // AF_INET version (IPv4)
    if (ifa->ifa_addr->sa_family == AF_INET) {
        return &((reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr))->sin_addr);
    }
    // AF_INET6 version (IPv6)
    return &((reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr))->sin6_addr);
}

int GetDevNameFromLocalIp(const std::string &ipAddr, std::string &devName)
{
    struct ifaddrs *ifaddr, *ifa;
    int family;

    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        return 1;
    }

    bool found = false;
    uint32_t expectedFlags = IFF_UP | IFF_BROADCAST | IFF_RUNNING | IFF_MULTICAST;
    for (ifa = ifaddr; ifa != NULL && found == false; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL)
            continue;

        family = ifa->ifa_addr->sa_family;

        if ((family == AF_INET || family == AF_INET6) && (expectedFlags & ifa->ifa_flags) == expectedFlags) {
            // Record device name in case the exact matching one is missing
            devName = std::string(ifa->ifa_name);
            std::string inetRes(INET6_ADDRSTRLEN, 0);
            if (0 == strcmp(ipAddr.c_str(), inet_ntop(ifa->ifa_addr->sa_family, GetInterfaceInAddr(ifa),
                                                      const_cast<char *>(inetRes.c_str()), inetRes.size()))) {
                VLOG(RDMA_LOG_LEVEL) << FormatString("find success devname=%s\n", ifa->ifa_name);
                found = true;
            }
        }
    }

    freeifaddrs(ifaddr);

    if (!found) {
        LOG(WARNING) << FormatString(
            "Device name of %s is either not found or not matching flags. Will use device name: %s", ipAddr, devName);
    }
    return 0;
}
}  // namespace datasystem