package main

import (
	"crypto/md5"
	_ "embed"
	"encoding/base64"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"math/rand/v2"
	"os"
	"strings"
	"syscall"

	"github.com/pkg/sftp"
	"github.com/spf13/pflag"
	"golang.org/x/crypto/ssh"
	"golang.org/x/term"
)

//go:embed hytop
var hytop_bin []byte

var (
	flagHost = pflag.String("host", "", "host to run hytop")
	flagPort = pflag.Uint16P("port", "p", 22, "ssh port")
)

func main() {
	pflag.Parse()
	if flagHost != nil {
		// 远程执行
		run_remote()
	} else {
		// 本地执行
		run_local()
	}
}

func run_local() {
	dir := fmt.Sprintf("/tmp/hytop-%s", get_rand_str())
	err := os.Mkdir(dir, 0777)
	if err != nil {
		log.Fatal(err)
	}
	fp := fmt.Sprintf("%s/%s", dir, "hytop")
	os.WriteFile(fp, hytop_bin, 0777)
	syscall.Exec(fp, []string{"hytop"}, os.Environ())
}

func run_remote() {

	home := os.Getenv("HOME")
	pk, err := os.ReadFile(home + "/.ssh/id_rsa")
	if err != nil {
		log.Fatalf("read file ${HOME}/.ssh/id_rsa error")
	}
	signer, err := ssh.ParsePrivateKey(pk)
	if err != nil {
		log.Fatalf("unable to parse private key: %v", err)
	}
	// 1. SSH 客户端配置
	config := &ssh.ClientConfig{
		User: os.Getenv("USER"),
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	// 2. 连接服务器
	client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", *flagHost, *flagPort), config)
	if err != nil {
		log.Fatal("连接失败: ", err)
	}
	defer client.Close()

	binpath, err := send_file(client)
	if err != nil {
		log.Fatal("error send file: ", err)
	}

	// 3. 创建会话
	session, err := client.NewSession()
	if err != nil {
		log.Fatal("创建会话失败: ", err)
	}
	defer session.Close()

	// 4. 关键：请求 PTY (伪终端)
	// 这让远程进程认为它运行在一个真实的终端窗口中
	fd := int(os.Stdin.Fd())
	state, err := term.MakeRaw(fd) // 将本地终端设为 Raw 模式以透传按键
	if err != nil {
		log.Fatal(err)
	}
	defer term.Restore(fd, state)

	w, h, _ := term.GetSize(fd)
	if err := session.RequestPty("xterm-256color", h, w, ssh.TerminalModes{
		ssh.ECHO:          1,
		ssh.TTY_OP_ISPEED: 14400,
		ssh.TTY_OP_OSPEED: 14400,
	}); err != nil {
		log.Fatal("请求PTY失败: ", err)
	}

	// 5. 绑定输入输出
	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	session.Stdin = os.Stdin

	// 6. 启动持久命令（如 bash）
	if err := session.Start(fmt.Sprintf(`PATH="${PATH}:/opt/hyhal/bin"; %s`, binpath)); err != nil {
		log.Fatal("启动Shell失败: ", err)
	}

	// 等待命令结束
	session.Wait()

	del_file(client, binpath)
}

func get_rand_str() string {
	i := rand.Uint64()
	b := make([]byte, 8)
	binary.BigEndian.PutUint64(b, i)
	src := md5.Sum(b)
	return strings.Trim(strings.ReplaceAll(base64.StdEncoding.EncodeToString(src[:]), "/", "a"), "=")
}

func send_file(sshCli *ssh.Client) (string, error) {
	sftp_client, err := sftp.NewClient(sshCli)
	if err != nil {
		return "", err
	}
	tmp_path := get_rand_str()
	err = sftp_client.Mkdir(fmt.Sprintf("/tmp/hytop-%s", tmp_path))
	if err != nil {
		sftp_client.Close()
		return "", err
	}
	res := fmt.Sprintf("/tmp/hytop-%s/hytop", tmp_path)
	sftp_file, err := sftp_client.Create(res)
	if err != nil {
		sftp_client.Close()
		return "", err
	}
	s, err := sftp_file.Write(hytop_bin)
	if s != len(hytop_bin) || err != nil {
		sftp_file.Close()
		sftp_client.Close()
		if err != nil {
			return "", err
		} else {
			return "", errors.New("sftp send file size not equal with raw file")
		}
	}
	sftp_file.Chmod(0777)
	sftp_file.Close()
	sftp_client.Close()
	return res, nil
}

func del_file(sshCli *ssh.Client, filePath string) error {
	sftp_client, err := sftp.NewClient(sshCli)
	if err != nil {
		return err
	}
	defer sftp_client.Close()
	return sftp_client.Remove(filePath)
}
