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

package ec200u

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

	"github.com/sirupsen/logrus"
	"github.com/vishvananda/netlink"
	"hnyfkj.com.cn/rtu/linux/baseapp"
)

// 禁用"eth0"网口, 使其不可用
func disableEth0() error {
	iface, err := net.InterfaceByName("eth0")
	if err != nil {
		return fmt.Errorf("未找到网口\"eth0\": %w", err)
	}

	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
}

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

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

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

	return nil
}

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

// 杀死"usb0"网口上的"udhcpc"进程(根据上次运行时它的PID-文件记录)
func killUSB0Udhcpc() error {
	usb0pid := baseapp.RUN_DIR + "/udhcpc.usb0.pid"
	data, err := os.ReadFile(usb0pid)
	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"后, 获取"usb0"网口分配到的IPv4地址, 并校检其合法性
func getUSB0Addr() (ip, mask string, err error) {
	iface, err := net.InterfaceByName("usb0")
	if err != nil {
		return "", "", fmt.Errorf("找不到网络接口\"usb0\": %w", err)
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return "", "", fmt.Errorf("获取\"usb0\"的地址失败: %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("在\"usb0\"上未找到有效的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
}

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

	for {
		select {
		case <-t.C:
			bExists, _ := udhcpcUSB0Exists()
			if !bExists { // 清场一次
				killAllUdhcpc()
				disableEth0()
			}
			if !bExists && dialupUSB0() == nil { // 重新进场
				ip, mask, _ := getUSB0Addr()
				baseapp.Logger.Warnf("[%s] \"usb0\"重新分配的地址: %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 waitUSB0Carrier(timeoutMs int) (bool, error) {
	interval, elapsed := 50, 0

	for elapsed < timeoutMs {
		data, err := os.ReadFile("/sys/class/net/usb0/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("usb0 carrier not up after %d ms", timeoutMs)
}

func Is4GCableConnected() (bool, error) {
	if up, _ := isInterfaceUp("usb0"); !up {
		err := enableUSB0()
		if err != nil {
			return false, err
		}
	}

	return waitUSB0Carrier(2000)
}
