// Author: NiuJiuRu
// Email: niujiuru@qq.com
// 以下函数需"root"权限运行

package air720u

import (
	"fmt"
	"io/fs"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/sirupsen/logrus"
	"hnyfkj.com.cn/rtu/bxs-sy/baseapp"
)

// 只保留需要使用的"eth2"网卡, 其它"eth*"网卡都被禁用掉(状态-离线)
func disableEthBut2() error {
	ifaces, err := net.Interfaces()
	if err != nil {
		return fmt.Errorf("获取网络接口列表失败: %w", err)
	}

	for _, iface := range ifaces {
		if !strings.HasPrefix(iface.Name, "eth") || iface.Name == "eth2" {
			continue
		}

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

	return nil
}

// 分配上网地址, 成功后"udhcpc"会保持后台运行并占用"/dev/ttyUSB0"
func dialupEth2() error {
	cmd1 := exec.Command("ip", "link", "set", "dev", "eth2", "up")
	cmd1.Stdout = baseapp.Logger.WriterLevel(logrus.DebugLevel)
	cmd1.Stderr = baseapp.Logger.WriterLevel(logrus.ErrorLevel)
	if err := cmd1.Run(); err != nil {
		return fmt.Errorf("启动网口\"eth2\"失败: %w", err)
	}

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

	return nil
}

// 检测"eth2"网口上的"udhcpc"后台进程是否正在运行, 只保持一个实例
func udhcpcEth2Exists() (bool, error) {
	eth2pid := baseapp.RUN_DIR + "/udhcpc.eth2.pid"
	data, err := os.ReadFile(eth2pid)
	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
}

// 杀死"eth2"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
func killEth2Udhcpc() error {
	eth2pid := baseapp.RUN_DIR + "/udhcpc.eth2.pid"
	data, err := os.ReadFile(eth2pid)
	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() {
	cmd := exec.Command("sh", "-c", `ps | grep '[u]dhcpc' | awk '{print $1}'`)
	output, err := cmd.Output()
	if err != nil {
		return // 没找到也不是错误
	}

	pids := strings.FieldsSeq(string(output))
	for pidStr := range pids {
		pid, err := strconv.Atoi(pidStr)
		if err != nil {
			continue
		}
		if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
			syscall.Kill(pid, syscall.SIGKILL)
		}
	}
}

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

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

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

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

		if ip4.IsPrivate() { // 判断得到IP地址是否合法
			return ip4.String(), net.IP(ipNet.Mask).String(), nil
		} else {
			return "", "", fmt.Errorf("分配给\"eth2\"的地址\"%s\"无效", ip4.String())
		}
	}

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

// 查找并杀死所有占用"/dev/ttyUSB0"设备的进程,返回被杀死进程的PID
func freeTTYUSB0() ([]int, error) {
	var pids []int
	seen := make(map[int]struct{})

	err := filepath.WalkDir("/proc", func(path string, d fs.DirEntry, walkErr error) error {
		if walkErr != nil {
			return nil
		}
		if filepath.Base(filepath.Dir(path)) != "fd" {
			return nil
		}
		link, err := os.Readlink(path)
		if err != nil || link != "/dev/ttyUSB0" {
			return nil
		}
		parts := strings.Split(path, "/")
		if len(parts) < 3 {
			return nil
		}
		pid, err := strconv.Atoi(parts[2])
		if err != nil {
			return nil
		}
		if _, exists := seen[pid]; exists {
			return nil // 已处理过这个PID, 跳过
		}
		seen[pid] = struct{}{}

		if err := syscall.Kill(pid, syscall.SIGTERM); err != nil && err != syscall.ESRCH {
			syscall.Kill(pid, syscall.SIGKILL)
		}

		pids = append(pids, pid)
		return nil
	})

	return pids, err
}

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

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