package utils

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
)

/*
获取Linux系统上所有用户的信息，如果系统安装了nis，还要解析nis的用户
*/

const (
	NISClient = "ypbind"
	NISCat    = "ypcat"
)

var SysUserInfo = sync.Map{}

// detectNis 探测系统是否为Nis的客户端，即解析是否存在ypbind命令，且ypbind命令在运行
func detectNis() (bool, error) {
	haveCmd, _ := DetectCmd(NISClient)
	if !haveCmd {
		return false, nil
	}
	ps, err := GetProcessByName(NISClient)
	if err != nil {
		return false, err
	}
	if len(ps) > 0 {
		return true, nil
	}
	return false, nil
}

// GetNisUsers 获取Nis中的所有用户信息
func GetNisUsers() ([]SysUser, error) {
	haveCmd, cmdPath := DetectCmd(NISCat)
	if !haveCmd {
		return make([]SysUser, 0), nil
	}
	output, err := exec.Command(cmdPath, "passwd").Output()
	if err != nil {
		return nil, err
	}
	return parseSysUser(string(output))
}

type SysUser struct {
	Name         string
	Uid          int
	Gid          int
	IsSystemUser bool
	HomeDir      string
	Shell        string
	GECOS        string // 用户信息
}

// parseSysUser 从字符串中解析系统用户信息
func parseSysUser(str string) ([]SysUser, error) {
	sysUsers := make([]SysUser, 0)
	lines := strings.Split(strings.Trim(str, "\n"), "\n")
	if len(lines) == 0 {
		return sysUsers, nil
	}
	for _, line := range lines {
		fields := strings.Split(line, ":")
		if len(fields) != 7 {
			continue
		}
		user := SysUser{}
		user.Name = strings.TrimSpace(fields[0])
		uid, err := strconv.Atoi(strings.TrimSpace(fields[2]))
		if err != nil {
			return nil, err
		}
		user.Uid = uid
		if user.Uid >= 1000 {
			user.IsSystemUser = false
		} else {
			user.IsSystemUser = true
		}
		gid, err := strconv.Atoi(strings.TrimSpace(fields[3]))
		if err != nil {
			return nil, err
		}
		user.Gid = gid
		user.GECOS = fields[4]
		user.HomeDir = strings.TrimSpace(fields[5])
		user.Shell = strings.TrimSpace(fields[6])
		sysUsers = append(sysUsers, user)
	}
	return sysUsers, nil
}

// GetSysUsers 获取系统上所有的用户
func GetSysUsers() error {
	detect, err := detectNis()
	if err != nil {
		return err
	}
	result := make([]SysUser, 0)
	if detect {
		u, _ := GetNisUsers()
		result = append(result, u...)
	}
	u, err := os.ReadFile("/etc/passwd")
	if err != nil {
		return nil
	}
	su, err := parseSysUser(string(u))
	if err != nil {
		return nil
	}
	result = append(result, su...)
	for _, user := range result {
		SysUserInfo.Store(user.Uid, &user)
	}
	return nil
}

func GetSysUserById(uid int) (*SysUser, error) {
	u, ok := SysUserInfo.Load(uid)
	if !ok {
		err := GetSysUsers()
		if err != nil {
			return nil, err
		}
		uu, o := SysUserInfo.Load(uid)
		if o && uu != nil {
			sysu, so := uu.(*SysUser)
			if so {
				return sysu, nil
			} else {
				return nil, fmt.Errorf("error loading sysuser, type error")
			}
		} else {
			return nil, nil
		}
	} else {
		sysu, o := u.(*SysUser)
		if !o {
			return nil, fmt.Errorf("error loading sysuser, type error")
		}
		return sysu, nil
	}
}
