package status

import (
	"fmt"
	"github.com/shirou/gopsutil/v3/net"
	net2 "net"
	"strings"
	"time"
)

type NetworkStatus struct {
	Name         string   `json:"name,omitempty"`
	InternalIPv4 []string `json:"internal_ip_v4,omitempty"`
	InternalIPv6 []string `json:"internal_ip_v6,omitempty"`
	ExternalIPv4 []string `json:"external_ip_v4,omitempty"`
	ExternalIPv6 []string `json:"external_ip_v6,omitempty"`
	Mac          string   `json:"mac,omitempty"`
	Sent         uint64   `json:"sent,omitempty"`
	Recv         uint64   `json:"recv,omitempty"`
	SentRate     string   `json:"sent_rate,omitempty"`
	RecvRate     string   `json:"recv_rate,omitempty"`
}

func GetNetworkStatus() (*NetworkStatus, error) {
	interfaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}
	var mainInterface net.InterfaceStat
	for _, intf := range interfaces {
		if strings.HasPrefix(intf.Name, "ens") || strings.HasPrefix(intf.Name, "eth") || strings.HasPrefix(intf.Name, "eno") {
			mainInterface = intf
			break
		}
	}
	var allAddrs []string
	log.DebugF("all main interface address are %v", mainInterface.Addrs)
	for _, addr := range mainInterface.Addrs {
		allAddrs = append(allAddrs, addr.Addr)
	}
	ipv4List, ipv6List := GetInternelIpAddrs(allAddrs)
	log.DebugF("ipv4 list are => %v, ipv6 list are => %v", ipv4List, ipv6List)

	counters, err := net.IOCounters(true)
	if err != nil {
		return nil, err
	}
	var sent uint64
	var recv uint64
	for _, counter := range counters {
		if counter.Name == mainInterface.Name {
			sent = counter.BytesSent
			recv = counter.BytesRecv
			break
		}
	}

	// 休眠3秒
	time.Sleep(3 * time.Second)

	var sentAfter uint64
	var recvAfter uint64
	countersAfter, err := net.IOCounters(true)
	if err != nil {
		return nil, err
	}
	for _, counter := range countersAfter {
		if counter.Name == mainInterface.Name {
			sentAfter = counter.BytesSent
			recvAfter = counter.BytesRecv
			break
		}
	}
	sendRate := fmt.Sprintf(formatBytes((sentAfter-sent)/3) + "/s")
	recvRate := fmt.Sprintf(formatBytes((recvAfter-recv)/3) + "/s")

	info := &NetworkStatus{
		Name:         mainInterface.Name,
		InternalIPv4: ipv4List,
		InternalIPv6: ipv6List,
		ExternalIPv4: nil,
		ExternalIPv6: nil,
		Mac:          mainInterface.HardwareAddr,
		Sent:         sent,
		Recv:         recv,
		SentRate:     sendRate,
		RecvRate:     recvRate,
	}
	return info, nil
}

func formatBytes(bytes uint64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := int64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}

func GetInternelIpAddrs(addresses []string) ([]string, []string) {
	var ipv4 []string
	var ipv6 []string
	for _, addr := range addresses {
		// it parse (0.0.0.0) not cidr
		ip, _, err := net2.ParseCIDR(addr)
		if err != nil {
			continue
		}
		if ip.To4() != nil {
			ipv4 = append(ipv4, addr)
		} else if ip.To16() != nil {
			ipv6 = append(ipv6, addr)
		}
	}
	return ipv4, ipv6
}
