package io.wdd.agent.executor.status;

import io.wdd.agent.config.utils.AgentCommonThreadPool;
import io.wdd.common.beans.status.NetworkInfo;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import oshi.hardware.NetworkIF;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 计算网卡接口的网速信息
 * 获取网卡接口的公网Ipv4和Ipv6信息
 */
@Service
@Lazy
public class NetworkStatusExecutor {

    private static final ArrayList<String> GET_PUBLIC_IPV4_ADDRESS_COMMAND;
    private static final ArrayList<String> GET_PUBLIC_IPV6_ADDRESS_COMMAND;

    static {
        GET_PUBLIC_IPV4_ADDRESS_COMMAND = new ArrayList<>(List.of(
                "wget",
                "-q",
                "-T10",
                "-O-",
                "https://ipinfo.io/ip"
        ));

        GET_PUBLIC_IPV6_ADDRESS_COMMAND = new ArrayList<>(List.of(
                "curl",
                "--max-time",
                "5",
                "-6",
                "https://ifconfig.co/ip"
        ));
    }


    public List<NetworkInfo> collect(List<NetworkIF> networkIFList, boolean updatePublicIpAddr) {

        // 获取网卡接口的公网Ipv4和Ipv6信息
        // 不需要，AgentBootUp通过命令行即可获取
        // 需要定时更新Agent的信息，根据Boolean来决定
        String[] publicIpAddress = new String[]{
                "publicIpv4",
                "publicIpv6"
        };
        if (updatePublicIpAddr) {
            publicIpAddress = collectAgentPublicIpAddress();
        }

        ArrayList<NetworkInfo> result = new ArrayList<>(16);


        // 计算网卡接口的网速信息
        for (NetworkIF networkIF : networkIFList) {
            // 计算网速
            NetworkInterfaceSpeedCallable speedCallable = new NetworkInterfaceSpeedCallable(networkIF);
            Future<String[]> netSpeedFuture = AgentCommonThreadPool.pool.submit(speedCallable);

            // 构建 NetworkInfo对象
            NetworkInfo singleNetworkInfo = NetworkInfo.mapFromNetworkIF(networkIF);

            try {
                String[] networkSpeedList = netSpeedFuture.get(5,
                                                               TimeUnit.SECONDS);
                // 注入网速信息
                singleNetworkInfo.setSendSpeed(networkSpeedList[0]);
                singleNetworkInfo.setRecvSpeed(networkSpeedList[1]);

            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                throw new RuntimeException(e);
            }

            if (updatePublicIpAddr) {
                // 注入公网IP地址信息
                singleNetworkInfo.setPublicIpv4Addr(publicIpAddress[0]);
                singleNetworkInfo.setPublicIpv6Addr(publicIpAddress[1]);
            }

            // 加入单个网卡的信息
            result.add(singleNetworkInfo);
        }


        return result;

    }


    /**
     * @return 第一位：公网Ipv4  第二位：公网Ipv6
     */
    private String[] collectAgentPublicIpAddress() {

        return new String[]{
                collectAgentPublicIpAddress(GET_PUBLIC_IPV4_ADDRESS_COMMAND),
                collectAgentPublicIpAddress(GET_PUBLIC_IPV6_ADDRESS_COMMAND)
        };

    }

    private String collectAgentPublicIpAddress(ArrayList<String> getPublicIPAddrCommandList) {
        try {


            ProcessBuilder processBuilder = new ProcessBuilder(getPublicIPAddrCommandList);

            Process process = processBuilder.start();

            boolean waitFor = process.waitFor(5,
                                              TimeUnit.SECONDS);
            String ipAddr = "null";

            if (ObjectUtils.isNotEmpty(waitFor) && process.exitValue() == 0) {

                ipAddr = new BufferedReader(new InputStreamReader(process.getInputStream())).readLine();
            }

            return ipAddr;

        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
