//go:build linux

package escrow

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"golang.org/x/sys/unix"
)

// runWithPassphrase runs a TTY-requiring command (PBS `key change-passphrase`, F-A1 in the spike
// findings) on a pty, feeding `passphrase` `reps` times (once per prompt) and DISCARDING all pty
// output so the echoed passphrase can never leak (F-A2). Linux-only — the agent runs on Proxmox
// hosts; a non-linux stub returns an error.
func runWithPassphrase(ctx context.Context, passphrase string, reps int, name string, args ...string) error {
	master, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
	if err != nil {
		return fmt.Errorf("escrow: open ptmx: %w", err)
	}
	defer master.Close()
	if err := unix.IoctlSetPointerInt(int(master.Fd()), unix.TIOCSPTLCK, 0); err != nil { // unlock
		return fmt.Errorf("escrow: unlock pty: %w", err)
	}
	ptn, err := unix.IoctlGetInt(int(master.Fd()), unix.TIOCGPTN)
	if err != nil {
		return fmt.Errorf("escrow: pty number: %w", err)
	}
	slave, err := os.OpenFile(fmt.Sprintf("/dev/pts/%d", ptn), os.O_RDWR|syscall.O_NOCTTY, 0)
	if err != nil {
		return fmt.Errorf("escrow: open pts: %w", err)
	}
	defer slave.Close()

	cmd := exec.CommandContext(ctx, name, args...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = slave, slave, slave
	// New session + the slave (fd 0) becomes the controlling terminal, so the child's tty prompts
	// read from / write to the pty rather than failing "no tty".
	cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true}
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("escrow: start %s: %w", name, err)
	}

	// Feed the passphrase once (the tty line discipline buffers both lines for the sequential
	// New/Verify prompts), then DISCARD everything the pty emits — the passphrase is echoed back
	// on the master fd and must never reach a log.
	go func() { _, _ = master.WriteString(strings.Repeat(passphrase+"\n", reps)) }()
	go func() { _, _ = io.Copy(io.Discard, master) }()

	return cmd.Wait()
}
