package netmgrd

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"

	"hnyfkj.com.cn/rtu/linux/baseapp"
)

// 禁用所有4G网卡, 使其不可用
func disable4GInterfaces() error {
	ifaces, err := net.Interfaces()
	if err != nil {
		return fmt.Errorf("获取网络接口列表失败: %w", err)
	}

	for _, iface := range ifaces {
		if iface.Name != "eth2" && iface.Name != "usb0" {
			continue
		}

		cmd := exec.Command("ip", "link", "set", "dev", iface.Name, "down")
		cmd.Stdout = baseapp.Logger.WriterLevel(logrus.InfoLevel)
		cmd.Stderr = baseapp.Logger.WriterLevel(logrus.WarnLevel)
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("关闭网口\"%s\"失败: %w", iface.Name, err)
		}
	}

	return nil
}

// 启动"eth0"网口, 使其变可用
func enableEth0() error {
	cmd := exec.Command("ip", "link", "set", "dev", "eth0", "up")
	cmd.Stdout = baseapp.Logger.WriterLevel(logrus.InfoLevel)
	cmd.Stderr = baseapp.Logger.WriterLevel(logrus.WarnLevel)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("启动网口\"eth0\"失败: %w", err)
	}
	return nil
}

// 启动DHCP客户端, 请求IP地址
func dialupEth0() error {
	if err := enableEth0(); err != nil {
		return err
	}

	eth0pid := baseapp.RUN_DIR + "/udhcpc.eth0.pid"
	cmd := exec.Command("udhcpc", "-b", "-i", "eth0", "-p", eth0pid)
	cmd.Stdout = baseapp.Logger.WriterLevel(logrus.InfoLevel)
	cmd.Stderr = baseapp.Logger.WriterLevel(logrus.WarnLevel)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("\"eth0\"请求地址失败: %w", err)
	}

	return nil
}

// 检测"eth0"网口上的"udhcpc"后台进程是否正在运行, 只保持一个实例
func udhcpcEth0Exists() (bool, error) {
	eth0pid := baseapp.RUN_DIR + "/udhcpc.eth0.pid"
	data, err := os.ReadFile(eth0pid)
	if err != nil {
		return false, err
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
	if err != nil {
		return false, err
	}
	err = syscall.Kill(pid, 0)
	if err == nil { // 存在
		return true, nil
	}
	if err == syscall.ESRCH { // 不存在
		return false, nil
	}
	return false, err
}

// 杀死"eth0"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
func killEth0Udhcpc() error {
	eth0pid := baseapp.RUN_DIR + "/udhcpc.eth0.pid"
	data, err := os.ReadFile(eth0pid)
	if err != nil {
		return err
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
	if err != nil {
		return err
	}
	if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
		syscall.Kill(pid, syscall.SIGKILL)
	}
	return nil
}

// 强制杀死所有运行中的"udhcpc"进程,包括驻留在后台运行的(SIGKILL)
func killAllUdhcpc() {
	entries, err := os.ReadDir("/proc")
	if err != nil {
		return
	}

	for _, entry := range entries {
		name := entry.Name()

		pid, err := strconv.Atoi(name)
		if err != nil {
			continue
		}

		comm, err := os.ReadFile("/proc/" + name + "/comm")
		if err != nil {
			continue
		}

		if strings.TrimSpace(string(comm)) == "udhcpc" {
			_ = syscall.Kill(pid, syscall.SIGKILL)
		} // if end
	} /// for end
}

// 运行"udhcpc"后, 获取"eth0"网口分配到的IPv4地址, 并校检其合法性
func getEth0Addr() (ip, mask string, err error) {
	iface, err := net.InterfaceByName("eth0")
	if err != nil {
		return "", "", fmt.Errorf("找不到网络接口\"eth0\": %w", err)
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return "", "", fmt.Errorf("获取\"eth0\"的地址失败: %w", err)
	}

	for _, addr := range addrs {
		ipNet, ok := addr.(*net.IPNet)
		if !ok {
			continue
		}

		ip4 := ipNet.IP.To4()
		if ip4 == nil {
			continue
		}

		return ip4.String(), net.IP(ipNet.Mask).String(), nil
	}

	return "", "", fmt.Errorf("在\"eth0\"上未找到有效的IPv4 地址")
}

// 定时检测"eth0"网卡上的"udhcpc"后台服务进程, 发现退出时自动拉起
func monitorEth0Udhcpc(exitCh <-chan struct{}) {
	t := time.NewTicker(time.Duration(5) * time.Second)
	defer t.Stop()

	for {
		select {
		case <-t.C:
			bExists, _ := udhcpcEth0Exists()
			if !bExists { // 清场一次
				killAllUdhcpc()
				disable4GInterfaces()
			}
			if !bExists && dialupEth0() == nil { // 重新进场
				ip, mask, _ := getEth0Addr()
				baseapp.Logger.Warnf("[%s] \"eth0\"重新分配的地址: %s/%s!", MODULE_NAME, ip, mask)
			}
		case <-exitCh:
			return
		} // select end
	} // for end
}

func isInterfaceUp(name string) (bool, error) {
	link, err := netlink.LinkByName(name)
	if err != nil {
		return false, err
	}
	return link.Attrs().Flags&net.FlagUp != 0, nil
}

func waitEth0Carrier(timeoutMs int) (bool, error) {
	interval, elapsed := 50, 0

	for elapsed < timeoutMs {
		data, err := os.ReadFile("/sys/class/net/eth0/carrier")
		if err == nil && strings.TrimSpace(string(data)) == "1" {
			return true, nil
		}
		time.Sleep(time.Millisecond * time.Duration(interval))
		elapsed += interval
	}

	return false, fmt.Errorf("eth0 carrier not up after %d ms", timeoutMs)
}

func isEth0CableConnected() (bool, error) {
	if up, _ := isInterfaceUp("eth0"); !up {
		err := enableEth0()
		if err != nil {
			return false, err
		}
	}

	return waitEth0Carrier(2000)
}

func SetupEth0ForManagement(cidr string) error {
	link, err := netlink.LinkByName("eth0")
	if err != nil {
		return err
	}

	if err = netlink.LinkSetUp(link); err != nil {
		return err
	}

	addr, err := netlink.ParseAddr(cidr)
	if err != nil {
		return err
	}

	if err = netlink.AddrAdd(link, addr); err != nil && err != syscall.EEXIST {
		return err
	}

	return nil
}
