package services

import (
	"bufio"
	"bytes"
	"lan-manager/server/config"
	"lan-manager/server/models"
	"strconv"
	"strings"
	"time"

	"golang.org/x/crypto/ssh"
)

func GetSSHInfo(ip string, port int, user, pass string) (*models.SSHInfoResult, error) {
	if port <= 0 {
		port = 22
	}
	cfg := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(pass),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         time.Duration(config.Load().SSHTimeout) * time.Second,
	}

	client, err := ssh.Dial("tcp", ip+":"+strconv.Itoa(port), cfg)
	if err != nil {
		return nil, err
	}
	defer client.Close()

	result := &models.SSHInfoResult{}

	// Hostname
	if out, err := runSSHCommand(client, "hostname"); err == nil {
		result.Hostname = strings.TrimSpace(out)
	}

	// OS Version
	if out, err := runSSHCommand(client, "cat /etc/os-release | grep PRETTY_NAME | cut -d= -f2 | tr -d '\"'"); err == nil && out != "" {
		result.OsVersion = strings.TrimSpace(out)
	} else if out, err := runSSHCommand(client, "uname -sr"); err == nil {
		result.OsVersion = strings.TrimSpace(out)
	}

	// CPU (two samples from /proc/stat for accurate real-time usage)
	cpuScript := `c1=$(awk '/^cpu /{print $2+$4,$2+$4+$5}' /proc/stat) && sleep 1 && c2=$(awk '/^cpu /{print $2+$4,$2+$4+$5}' /proc/stat) && awk 'BEGIN{split("'"$c1"'", a, " "); split("'"$c2"'", b, " "); if(b[2]-a[2]>0) printf "%.1f", 100*(b[1]-a[1])/(b[2]-a[2]); else print "0.0"}'`
	if out, err := runSSHCommand(client, cpuScript); err == nil && out != "" {
		cpuUsage := strings.TrimSpace(out)
		if cpuUsage == "" {
			cpuUsage = "0.0"
		}
		cores := ""
		if cout, err := runSSHCommand(client, "nproc"); err == nil {
			cores = strings.TrimSpace(cout)
		}
		result.CPU = cpuUsage + "%"
		if cores != "" {
			result.CPU += " (" + cores + " cores)"
		}
		result.RawCPUInfo = result.CPU
	}

	// Memory
	if out, err := runSSHCommand(client, "free -m | awk 'NR==2{printf \"%.1f/%.1f (%.0f%%)\", $3/1024,$2/1024,$3*100/$2 }'"); err == nil && out != "" {
		result.Memory = strings.TrimSpace(out)
		result.RawMemoryInfo = result.Memory
	}

	// Disk (exclude virtual filesystems, keep all real partitions)
	// Use df -hP to prevent long filesystem names from wrapping to next line
	if out, err := runSSHCommand(client, "df -hP | awk 'NR>1 && $1 !~ /^(tmpfs|devtmpfs|squashfs|overlay|efivarfs|tracefs|securityfs|pstore|bpf|configfs|fusectl|mqueue|hugetlbfs|rpc_pipefs|nfsd|binfmt_misc|autofs|devpts|proc|sysfs|cgroup|cgroup2|ramfs)$/ {printf \"%s %s/%s (%s)  \", $6,$3,$2,$5}'"); err == nil && out != "" {
		result.Disk = strings.TrimSpace(out)
		result.RawDiskInfo = result.Disk
	}

	// Uptime
	if out, err := runSSHCommand(client, "uptime -p 2>/dev/null || uptime | awk -F',' '{print $1}'"); err == nil {
		result.Uptime = strings.TrimSpace(out)
	}

	// Listen ports
	if out, err := runSSHCommand(client, "ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq"); err == nil && out != "" {
		ports := []int{}
		scanner := bufio.NewScanner(strings.NewReader(out))
		for scanner.Scan() {
			p := strings.TrimSpace(scanner.Text())
			if p == "" {
				continue
			}
			if pi, err := strconv.Atoi(p); err == nil {
				ports = append(ports, pi)
			}
		}
		result.ListenPorts = ports
	}

	return result, nil
}

func runSSHCommand(client *ssh.Client, cmd string) (string, error) {
	session, err := client.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()
	var b bytes.Buffer
	session.Stdout = &b
	err = session.Run(cmd)
	return b.String(), err
}
