package proxmox

import (
	"bytes"
	"context"
	"crypto/ed25519"
	"crypto/rand"
	"errors"
	"log/slog"
	"net"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"

	"git.cloudinit.dev/coreci/orca/internal/certpaths"
	"time"

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

	"git.cloudinit.dev/coreci/orca/internal/security"
)

func TestSudoersContent(t *testing.T) {
	content := sudoersContent("orca")

	if !strings.Contains(content, "NOPASSWD: NOEXEC: /usr/bin/pct") {
		t.Error("missing NOEXEC on pct (AD-020)")
	}
	if !strings.Contains(content, "NOPASSWD: NOEXEC: /usr/bin/qm") {
		t.Error("missing NOEXEC on qm (AD-020)")
	}

	if strings.Contains(content, "apt-get") {
		t.Error("apt-get must NOT be in sudoers (REQ-134/F22: operator runs apt-get out-of-band)")
	}
	if strings.Contains(content, "dpkg") {
		t.Error("dpkg must NOT be in sudoers (REQ-134/F22: operator runs dpkg out-of-band)")
	}

	for _, line := range strings.Split(content, "\n") {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "#") || trimmed == "" {
			continue
		}
		if strings.Contains(trimmed, "pvesh") {
			t.Errorf("pvesh must be EXCLUDED from sudoers command lines (AD-020): %s", trimmed)
		}
	}

	if !strings.HasPrefix(content, "# /etc/sudoers.d/orca") {
		t.Error("missing managed-by-orca header")
	}
	if !strings.Contains(content, "orca ALL=(root)") {
		t.Error("missing orca user in sudoers")
	}
}

func TestSudoersContent_CustomUser(t *testing.T) {
	content := sudoersContent("custom-orca")
	if !strings.Contains(content, "custom-orca ALL=(root)") {
		t.Error("missing custom-orca user in sudoers")
	}
}

func TestOrcaOperatorPrivileges(t *testing.T) {
	privs := strings.Fields(OrcaOperatorPrivileges)
	expected := map[string]bool{
		"VM.Audit":                true,
		"Datastore.AllocateSpace": true,
		"SDN.Use":                 true,
	}
	if len(privs) != 3 {
		t.Errorf("expected 3 privileges, got %d: %v", len(privs), privs)
	}
	for _, p := range privs {
		if !expected[p] {
			t.Errorf("unexpected privilege %q", p)
		}
	}
}

func TestBootstrapProxmox_Validation(t *testing.T) {
	ctx := context.Background()

	_, err := BootstrapProxmox(ctx, Options{SSHKeyPath: certpaths.SSHKeyPath()})
	if err == nil || !strings.Contains(err.Error(), "host is required") {
		t.Errorf("expected host-required error, got %v", err)
	}

	_, err = BootstrapProxmox(ctx, Options{Host: "10.0.0.1"})
	if err == nil || !strings.Contains(err.Error(), "SSH key path is required") {
		t.Errorf("expected SSH-key-required error, got %v", err)
	}
}

// TestBootstrapProxmox_RejectsInvalidProxmoxUser verifies that a
// ProxmoxUser containing shell metacharacters is rejected before any
// SSH dial (F10a: sudoers/shell injection guard).
func TestBootstrapProxmox_RejectsInvalidProxmoxUser(t *testing.T) {
	ctx := context.Background()
	bad := []string{"orca; rm -rf /", "orca$(whoami)", "orca`id`", "orca user", "1orca"}
	for _, b := range bad {
		_, err := BootstrapProxmox(ctx, Options{Host: "10.0.0.1", SSHKeyPath: certpaths.SSHKeyPath(), ProxmoxUser: b})
		if err == nil {
			t.Errorf("expected error for invalid ProxmoxUser %q, got nil", b)
			continue
		}
		if !strings.Contains(err.Error(), "invalid ProxmoxUser") {
			t.Errorf("error should mention invalid ProxmoxUser for %q, got: %v", b, err)
		}
	}
}

// TestBootstrapProxmox_RejectsInvalidProxmoxRole verifies that a
// ProxmoxRole containing shell metacharacters is rejected before any
// SSH dial (F10a: sudoers/shell injection guard).
func TestBootstrapProxmox_RejectsInvalidProxmoxRole(t *testing.T) {
	ctx := context.Background()
	bad := []string{"role; flush", "role$(id)", "role`whoami`", "role name", "1role"}
	for _, b := range bad {
		_, err := BootstrapProxmox(ctx, Options{Host: "10.0.0.1", SSHKeyPath: certpaths.SSHKeyPath(), ProxmoxRole: b})
		if err == nil {
			t.Errorf("expected error for invalid ProxmoxRole %q, got nil", b)
			continue
		}
		if !strings.Contains(err.Error(), "invalid ProxmoxRole") {
			t.Errorf("error should mention invalid ProxmoxRole for %q, got: %v", b, err)
		}
	}
}

func TestDefaultOptions(t *testing.T) {
	if DefaultProxmoxUser != "orca" {
		t.Errorf("DefaultProxmoxUser = %q, want orca", DefaultProxmoxUser)
	}
	if DefaultProxmoxRole != "OrcaOperator" {
		t.Errorf("DefaultProxmoxRole = %q, want OrcaOperator", DefaultProxmoxRole)
	}
	if DefaultSSHPort != 22 {
		t.Errorf("DefaultSSHPort = %d, want 22", DefaultSSHPort)
	}
}

type mockSSHDialer struct {
	client   *ssh.Client
	err      error
	calls    int
	lastAddr string
	lastCfg  *ssh.ClientConfig
}

func (m *mockSSHDialer) DialContext(ctx context.Context, network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
	m.calls++
	m.lastAddr = addr
	m.lastCfg = config
	if m.err != nil {
		return nil, m.err
	}
	return m.client, nil
}

func setupORCAHome(t *testing.T) string {
	t.Helper()
	dir := t.TempDir()
	t.Setenv("ORCA_HOME", dir)
	knownHosts := filepath.Join(dir, "known_hosts")
	if err := os.WriteFile(knownHosts, []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}
	return dir
}

func TestBootstrapProxmox_SSHAuthFailure(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("ssh: handshake failed: ssh: unable to authenticate")}

	setupORCAHome(t)

	_, err := BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
	})
	if err == nil {
		t.Fatal("expected error, got nil")
	}
	if !strings.Contains(err.Error(), "ssh") {
		t.Errorf("error should mention ssh, got: %v", err)
	}
	if !strings.Contains(err.Error(), "ssh dial") {
		t.Errorf("error should mention ssh dial, got: %v", err)
	}
}

func TestBootstrapProxmox_SSHDialCalledWithCorrectAddr(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	dialer := &mockSSHDialer{err: errors.New("connection refused")}
	sshDialer = dialer

	setupORCAHome(t)

	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.42",
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    2222,
	})
	if dialer.calls != 1 {
		t.Errorf("dialer calls = %d, want 1", dialer.calls)
	}
	if dialer.lastAddr != "10.0.0.42:2222" {
		t.Errorf("dial addr = %q, want 10.0.0.42:2222", dialer.lastAddr)
	}
}

func TestBootstrapProxmox_DefaultSSHPort(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	dialer := &mockSSHDialer{err: errors.New("connection refused")}
	sshDialer = dialer

	setupORCAHome(t)

	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.99",
		SSHKeyPath: certpaths.SSHKeyPath(),
	})
	if dialer.lastAddr != "10.0.0.99:22" {
		t.Errorf("dial addr = %q, want 10.0.0.99:22 (default port)", dialer.lastAddr)
	}
}

func TestBootstrapProxmox_CustomSSHUser(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	dialer := &mockSSHDialer{err: errors.New("connection refused")}
	sshDialer = dialer

	setupORCAHome(t)

	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHUser:    "custom-admin",
	})
	if dialer.calls != 1 {
		t.Errorf("dialer calls = %d, want 1", dialer.calls)
	}
	if dialer.lastCfg == nil || dialer.lastCfg.User != "custom-admin" {
		t.Errorf("ssh user not propagated, got %+v", dialer.lastCfg)
	}
}

func TestBootstrapProxmox_SSHKeyGenerated(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("connection refused")}

	dir := setupORCAHome(t)

	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
	})

	keyPath := filepath.Join(dir, "orca_ssh_key")
	pubPath := filepath.Join(dir, "orca_ssh_key.pub")
	if _, err := os.Stat(keyPath); err != nil {
		t.Errorf("SSH key not generated at %s: %v", keyPath, err)
	}
	if _, err := os.Stat(pubPath); err != nil {
		t.Errorf("SSH pub not generated at %s: %v", pubPath, err)
	}
}

func TestBootstrapProxmox_KnownHostsFileCreated(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("connection refused")}

	dir := setupORCAHome(t)

	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
	})

	knownHosts := filepath.Join(dir, "known_hosts")
	if _, err := os.Stat(knownHosts); err != nil {
		t.Errorf("known_hosts not created at %s: %v", knownHosts, err)
	}
}

func TestBootstrapProxmox_NilLogger(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("connection refused")}

	setupORCAHome(t)

	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("nil logger panicked: %v", r)
		}
	}()
	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
		Logger:     nil,
	})
}

func TestBootstrapProxmox_CustomLogger(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("connection refused")}

	setupORCAHome(t)

	var buf bytes.Buffer
	log := slog.New(slog.NewTextHandler(&buf, nil))

	defer func() {
		if r := recover(); r != nil {
			t.Fatalf("custom logger panicked: %v", r)
		}
	}()
	_, _ = BootstrapProxmox(context.Background(), Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
		Logger:     log,
	})
	_ = buf.String()
}

func TestBootstrapProxmox_ContextCancelled(t *testing.T) {
	orig := sshDialer
	defer func() { sshDialer = orig }()
	sshDialer = &mockSSHDialer{err: errors.New("connection refused")}

	setupORCAHome(t)

	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	_, err := BootstrapProxmox(ctx, Options{
		Host:       "10.0.0.1",
		SSHKeyPath: certpaths.SSHKeyPath(),
	})
	if err == nil {
		t.Fatal("expected error with cancelled context")
	}
}

func TestDeployPubKey_EmptyPubLine(t *testing.T) {
	err := deployPubKey("orca", "")
	if err == nil {
		t.Error("expected error for empty pub line")
	}
	if !strings.Contains(err.Error(), "empty pub line") {
		t.Errorf("error should mention empty pub line, got: %v", err)
	}
}

func TestDeployPubKey_WhitespaceOnlyPubLine(t *testing.T) {
	err := deployPubKey("orca", "   \n  \t  ")
	if err == nil {
		t.Error("expected error for whitespace-only pub line")
	}
}

func TestBootstrapProxmox_FullFlow_IdempotentReRun(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()

	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}

	orig := sshDialer
	defer func() { sshDialer = orig }()
	origRunner := sessionRunner
	defer func() { sessionRunner = origRunner }()

	host, _, _ := net.SplitHostPort(srv.addr())
	sshDialer = &funcDialer{fn: func(ctx context.Context, network, addr string, config *ssh.ClientConfig) (*ssh.Client, error) {
		return fakeSSHClient(t, srv), nil
	}}

	for i := 0; i < 2; i++ {
		sessionRunner = nil
		if _, err := BootstrapProxmox(t.Context(), Options{
			Host:       host,
			SSHKeyPath: certpaths.SSHKeyPath(),
		}); err != nil {
			t.Fatalf("bootstrap run %d: %v", i+1, err)
		}
	}
}

func TestBootstrapProxmox_FullFlow_NoPasswordInLogs(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()

	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}

	orig := sshDialer
	defer func() { sshDialer = orig }()
	origRunner := sessionRunner
	defer func() { sessionRunner = origRunner }()
	sessionRunner = nil
	sshDialer = &staticDialer{client: fakeSSHClient(t, srv)}

	host, _, _ := net.SplitHostPort(srv.addr())

	var logBuf bytes.Buffer
	_, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		Logger:     slog.New(slog.NewTextHandler(&logBuf, nil)),
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox: %v", err)
	}
	out := logBuf.String()
	if strings.Contains(out, "super-secret-pw-12345") {
		t.Errorf("password leaked into logs (D-031): %s", out)
	}
}

func TestBootstrapProxmox_FullFlow_ValidateSudoersFails(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()
	srv.forceSudoersInvalid = true

	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}

	orig := sshDialer
	defer func() { sshDialer = orig }()
	origRunner := sessionRunner
	defer func() { sessionRunner = origRunner }()
	sessionRunner = nil
	sshDialer = &staticDialer{client: fakeSSHClient(t, srv)}

	host, _, _ := net.SplitHostPort(srv.addr())

	_, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
	})
	if err == nil {
		t.Fatal("expected error for invalid sudoers")
	}
	if !strings.Contains(err.Error(), "validate sudoers") {
		t.Errorf("error should mention validate sudoers, got: %v", err)
	}
}

func TestDefaultSSHDialer_DialContext_ConnectionRefused(t *testing.T) {
	d := defaultSSHDialer{}
	cfg := &ssh.ClientConfig{
		User:            "root",
		Auth:            []ssh.AuthMethod{ssh.Password("pw")},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		Timeout:         200 * time.Millisecond,
	}
	_, err := d.DialContext(context.Background(), "tcp", "127.0.0.1:1", cfg)
	if err == nil {
		t.Fatal("expected error for connection refused")
	}
}

func TestBootstrapProxmox_FullFlow_CreateLinuxUserFails(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()

	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}

	orig := sshDialer
	defer func() { sshDialer = orig }()
	origRunner := sessionRunner
	defer func() { sessionRunner = origRunner }()
	sessionRunner = nil
	sshDialer = &staticDialer{client: fakeSSHClient(t, srv)}

	host, _, _ := net.SplitHostPort(srv.addr())

	// ProxmoxUser=root exercises the /root home branch in deployPubKey.
	_, err := BootstrapProxmox(t.Context(), Options{
		Host:        host,
		SSHKeyPath:  certpaths.SSHKeyPath(),
		ProxmoxUser: "root",
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox with ProxmoxUser=root: %v", err)
	}
}

func TestSSHSessionRunner_CombinedOutput_NewSessionError(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()
	conn := fakeSSHClient(t, srv)
	conn.Close()
	r := &sshSessionRunner{client: conn}
	_, err := r.CombinedOutput("echo hi")
	if err == nil {
		t.Fatal("expected error from NewSession on closed client")
	}
	if !strings.Contains(err.Error(), "new session") {
		t.Errorf("error should mention new session, got: %v", err)
	}
}

// TestPinnedHostKeyCallback_Match verifies the pinned callback returns
// nil when the server-presented key matches the operator-supplied
// fingerprint (T02.5, REQ-058).
func TestPinnedHostKeyCallback_Match(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()
	host, port, _ := net.SplitHostPort(srv.addr())
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}
	expectedFP := security.SSHFingerprintSHA256(hostKey)

	var captured ssh.PublicKey
	cb, err := pinnedHostKeyCallback(expectedFP, &captured)
	if err != nil {
		t.Fatalf("pinnedHostKeyCallback: %v", err)
	}
	if err := cb(host+":"+port, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey); err != nil {
		t.Errorf("match callback returned error: %v", err)
	}
	if !bytes.Equal(captured.Marshal(), hostKey.Marshal()) {
		t.Error("captured key does not match server host key")
	}
}

// TestPinnedHostKeyCallback_Mismatch verifies the pinned callback fails
// closed on mismatch (T02.5, REQ-058).
func TestPinnedHostKeyCallback_Mismatch(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()
	host, _, _ := net.SplitHostPort(srv.addr())
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}

	cb, err := pinnedHostKeyCallback("SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", nil)
	if err != nil {
		t.Fatalf("pinnedHostKeyCallback: %v", err)
	}
	err = cb(host+":22", &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey)
	if err == nil {
		t.Fatal("expected mismatch error, got nil")
	}
	if !strings.Contains(err.Error(), "REQ-058") {
		t.Errorf("mismatch error should mention REQ-058, got: %v", err)
	}
}

// TestPinnedHostKeyCallback_RejectsRawHex verifies the constructor
// rejects a non-SHA256:-prefixed fingerprint (T02.5, D-045).
func TestPinnedHostKeyCallback_RejectsRawHex(t *testing.T) {
	_, err := pinnedHostKeyCallback("abcdef0123456789", nil)
	if err == nil {
		t.Fatal("expected error for raw hex fingerprint, got nil")
	}
	if !strings.Contains(err.Error(), "SHA256:") {
		t.Errorf("error should mention SHA256: prefix requirement, got: %v", err)
	}
}

// TestTOFUHostKeyCallback_FirstConnectCapturesKey verifies that on
// first connect (empty known_hosts) the TOFU callback captures the
// server key, writes it to known_hosts, and allows the dial (T02.6 —
// v0.6 ship-defect fix).
func TestTOFUHostKeyCallback_FirstConnectCapturesKey(t *testing.T) {
	home := setupORCAHome(t) // empty known_hosts
	srv := newFakeSSHServer(t)
	defer srv.close()
	host, port, _ := net.SplitHostPort(srv.addr())
	addr := host + ":" + port
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}

	cb, err := TOFUHostKeyCallback(addr, nil)
	if err != nil {
		t.Fatalf("TOFUHostKeyCallback: %v", err)
	}
	if err := cb(addr, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey); err != nil {
		t.Fatalf("first-connect callback returned error: %v", err)
	}
	data, err := os.ReadFile(filepath.Join(home, "known_hosts"))
	if err != nil {
		t.Fatalf("read known_hosts: %v", err)
	}
	if len(data) == 0 {
		t.Fatal("known_hosts is empty — TOFU capture did not write the key (v0.6 ship-defect not fixed)")
	}
	if !strings.Contains(string(data), knownhosts.Normalize(addr)) {
		t.Errorf("known_hosts missing the normalized addr %q: %s", knownhosts.Normalize(addr), data)
	}
	if !strings.Contains(string(data), hostKey.Type()) {
		t.Errorf("known_hosts missing the host key type %q: %s", hostKey.Type(), data)
	}
}

// TestTOFUHostKeyCallback_SecondConnectMatches verifies that on a
// second connect (known_hosts already has the key) the TOFU callback
// matches and returns nil (T02.6).
func TestTOFUHostKeyCallback_SecondConnectMatches(t *testing.T) {
	setupORCAHome(t)
	srv := newFakeSSHServer(t)
	defer srv.close()
	host, port, _ := net.SplitHostPort(srv.addr())
	addr := host + ":" + port
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}

	// First connect: capture + write.
	cb1, err := TOFUHostKeyCallback(addr, nil)
	if err != nil {
		t.Fatalf("TOFUHostKeyCallback #1: %v", err)
	}
	if err := cb1(addr, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey); err != nil {
		t.Fatalf("first connect: %v", err)
	}

	// Second connect: the fresh knownhosts.New reads the written key.
	cb2, err := TOFUHostKeyCallback(addr, nil)
	if err != nil {
		t.Fatalf("TOFUHostKeyCallback #2: %v", err)
	}
	if err := cb2(addr, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey); err != nil {
		t.Fatalf("second connect should match, got: %v", err)
	}
}

// TestTOFUHostKeyCallback_MismatchFails verifies that on a mismatch
// (known_hosts has a different key) the TOFU callback fails closed
// (MITM detection) (T02.6).
func TestTOFUHostKeyCallback_MismatchFails(t *testing.T) {
	setupORCAHome(t)
	srv := newFakeSSHServer(t)
	defer srv.close()
	host, port, _ := net.SplitHostPort(srv.addr())
	addr := host + ":" + port
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}

	// Capture the real key first so known_hosts is populated.
	cb1, err := TOFUHostKeyCallback(addr, nil)
	if err != nil {
		t.Fatalf("TOFUHostKeyCallback #1: %v", err)
	}
	if err := cb1(addr, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, hostKey); err != nil {
		t.Fatalf("first connect: %v", err)
	}

	// Generate a different key + present it: callback must fail.
	pub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("ed25519 gen: %v", err)
	}
	altKey, err := ssh.NewPublicKey(pub)
	if err != nil {
		t.Fatalf("new pub: %v", err)
	}

	cb2, err := TOFUHostKeyCallback(addr, nil)
	if err != nil {
		t.Fatalf("TOFUHostKeyCallback #2: %v", err)
	}
	err = cb2(addr, &net.TCPAddr{IP: net.ParseIP(host), Port: 22}, altKey)
	if err == nil {
		t.Fatal("expected mismatch error, got nil")
	}
}

// TestBootstrapProxmox_PopulatesHostKeyFingerprint verifies that after
// a successful bootstrap via TOFU, Result.HostKeyFingerprint is
// non-empty and SHA256:-prefixed (T02.7).
func TestBootstrapProxmox_PopulatesHostKeyFingerprint(t *testing.T) {
	srv := newFakeSSHServer(t)
	defer srv.close()

	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}

	orig := sshDialer
	defer func() { sshDialer = orig }()
	origRunner := sessionRunner
	defer func() { sessionRunner = origRunner }()
	sessionRunner = nil
	// Use the real dialer so the TOFU HostKeyCallback actually runs
	// against the fake server (a static dialer with an insecure client
	// would bypass the callback and leave HostKeyFingerprint empty).
	sshDialer = defaultSSHDialer{}

	host, port, _ := net.SplitHostPort(srv.addr())
	portNum, _ := strconv.Atoi(port)

	result, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox: %v", err)
	}
	if result.HostKeyFingerprint == "" {
		t.Fatal("Result.HostKeyFingerprint is empty")
	}
	if !strings.HasPrefix(result.HostKeyFingerprint, "SHA256:") {
		t.Errorf("Result.HostKeyFingerprint = %q, want SHA256: prefix", result.HostKeyFingerprint)
	}
}

// TestResetHostKey_RemovesTargetLines verifies that ResetHostKey
// removes all known_hosts lines for the target host while leaving
// other hosts' lines intact (T02.8, REQ-059, D-046).
func TestResetHostKey_RemovesTargetLines(t *testing.T) {
	home := setupORCAHome(t)
	path := filepath.Join(home, "known_hosts")
	original := []byte("[10.0.0.1]:22 ssh-ed25519 AAAAKEY1 host1\n" +
		"10.0.0.1 ssh-ed25519 AAAAKEY1ALT host1-alt\n" +
		"[10.0.0.2]:22 ssh-ed25519 AAAAKEY2 host2\n")
	if err := os.WriteFile(path, original, 0o600); err != nil {
		t.Fatalf("write known_hosts: %v", err)
	}

	if err := ResetHostKey("10.0.0.1"); err != nil {
		t.Fatalf("ResetHostKey: %v", err)
	}

	data, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read known_hosts: %v", err)
	}
	result := string(data)
	if strings.Contains(result, "AAAAKEY1") {
		t.Errorf("target host key line not removed: %s", result)
	}
	if strings.Contains(result, "AAAAKEY1ALT") {
		t.Errorf("target host alt key line not removed: %s", result)
	}
	if !strings.Contains(result, "AAAAKEY2") {
		t.Errorf("other host's line was removed (should be intact): %s", result)
	}
}

// TestResetHostKey_NoMatchingLinesIsNoop verifies that ResetHostKey is
// a no-op when no lines match (T02.8).
func TestResetHostKey_NoMatchingLinesIsNoop(t *testing.T) {
	home := setupORCAHome(t)
	path := filepath.Join(home, "known_hosts")
	original := []byte("[10.0.0.2]:22 ssh-ed25519 AAAAKEY2 host2\n")
	if err := os.WriteFile(path, original, 0o600); err != nil {
		t.Fatalf("write known_hosts: %v", err)
	}

	if err := ResetHostKey("10.0.0.99"); err != nil {
		t.Fatalf("ResetHostKey: %v", err)
	}

	data, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read known_hosts: %v", err)
	}
	if string(data) != string(original) {
		t.Errorf("known_hosts changed on no-match: got %q, want %q", data, original)
	}
}

// TestResetHostKey_MissingFileIsNoop verifies ResetHostKey returns nil
// when known_hosts does not exist (T02.8).
func TestResetHostKey_MissingFileIsNoop(t *testing.T) {
	setupORCAHome(t)
	if err := ResetHostKey("10.0.0.1"); err != nil {
		t.Errorf("ResetHostKey on missing file should be no-op, got: %v", err)
	}
}

// TestResetHostKey_EmptyHostErrors verifies ResetHostKey rejects an
// empty host (T02.8).
func TestResetHostKey_EmptyHostErrors(t *testing.T) {
	if err := ResetHostKey(""); err == nil {
		t.Error("expected error for empty host, got nil")
	}
}

// bootstrapE2ESetup wires the real dialer against a fake SSH server so
// the full HostKeyCallback path (pinned or TOFU) runs end-to-end through
// BootstrapProxmox. Returns the host, port, and server (for fingerprint
// computation). The known_hosts file is created empty in the temp
// ORCA_HOME.
func bootstrapE2ESetup(t *testing.T) (srv *fakeSSHServer, host, port string) {
	t.Helper()
	srv = newFakeSSHServer(t)
	t.Cleanup(srv.close)
	home := t.TempDir()
	t.Setenv("ORCA_HOME", home)
	if err := os.WriteFile(filepath.Join(home, "known_hosts"), []byte{}, 0o600); err != nil {
		t.Fatalf("create known_hosts: %v", err)
	}
	orig := sshDialer
	t.Cleanup(func() { sshDialer = orig })
	origRunner := sessionRunner
	t.Cleanup(func() { sessionRunner = origRunner })
	sessionRunner = nil
	sshDialer = defaultSSHDialer{}
	host, port, _ = net.SplitHostPort(srv.addr())
	return srv, host, port
}

// TestBootstrapE2E_PinnedFingerprintCorrect verifies that
// --host-key-fingerprint with the correct pin (T02.10 case 1) succeeds
// end-to-end and Result.HostKeyFingerprint equals the pinned value.
func TestBootstrapE2E_PinnedFingerprintCorrect(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}
	pin := security.SSHFingerprintSHA256(hostKey)
	portNum, _ := strconv.Atoi(port)

	result, err := BootstrapProxmox(t.Context(), Options{
		Host:               host,
		SSHKeyPath:         certpaths.SSHKeyPath(),
		SSHPort:            portNum,
		HostKeyFingerprint: pin,
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox with correct pin: %v", err)
	}
	if result.HostKeyFingerprint != pin {
		t.Errorf("Result.HostKeyFingerprint = %q, want %q (pinned value)",
			result.HostKeyFingerprint, pin)
	}
}

// TestBootstrapE2E_PinnedFingerprintWrong verifies that
// --host-key-fingerprint with a wrong pin (T02.10 case 2) fails fast
// with the REQ-058 mismatch error, before any SSH session commands run.
func TestBootstrapE2E_PinnedFingerprintWrong(t *testing.T) {
	_, host, port := bootstrapE2ESetup(t)
	portNum, _ := strconv.Atoi(port)
	wrong := "SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="

	_, err := BootstrapProxmox(t.Context(), Options{
		Host:               host,
		SSHKeyPath:         certpaths.SSHKeyPath(),
		SSHPort:            portNum,
		HostKeyFingerprint: wrong,
	})
	if err == nil {
		t.Fatal("expected error for wrong pin, got nil")
	}
	if !strings.Contains(err.Error(), "REQ-058") {
		t.Errorf("error should mention REQ-058, got: %v", err)
	}
}

// TestBootstrapE2E_TOFUFirstConnectCapturesKey verifies that with no
// --host-key-fingerprint on a first connect (empty known_hosts) (T02.10
// case 3) the TOFU callback captures the key, writes known_hosts, and
// bootstrap succeeds — exercised end-to-end through BootstrapProxmox.
func TestBootstrapE2E_TOFUFirstConnectCapturesKey(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}
	portNum, _ := strconv.Atoi(port)
	home := os.Getenv("ORCA_HOME")
	knownHostsPath := filepath.Join(home, "known_hosts")

	before, _ := os.ReadFile(knownHostsPath)
	if len(before) != 0 {
		t.Fatalf("precondition: known_hosts not empty: %q", before)
	}

	result, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox first connect: %v", err)
	}

	data, err := os.ReadFile(knownHostsPath)
	if err != nil {
		t.Fatalf("read known_hosts: %v", err)
	}
	if len(data) == 0 {
		t.Fatal("known_hosts empty — TOFU did not capture the key end-to-end")
	}
	expectedFP := security.SSHFingerprintSHA256(hostKey)
	if result.HostKeyFingerprint != expectedFP {
		t.Errorf("Result.HostKeyFingerprint = %q, want %q", result.HostKeyFingerprint, expectedFP)
	}
}

// TestBootstrapE2E_TOFUSecondConnectMatches verifies that a second
// connect (known_hosts already has the key from the first connect)
// (T02.10 case 4) matches and succeeds end-to-end.
func TestBootstrapE2E_TOFUSecondConnectMatches(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	portNum, _ := strconv.Atoi(port)

	for i := 0; i < 2; i++ {
		sessionRunner = nil
		if _, err := BootstrapProxmox(t.Context(), Options{
			Host:       host,
			SSHKeyPath: certpaths.SSHKeyPath(),
			SSHPort:    portNum,
		}); err != nil {
			t.Fatalf("bootstrap run %d: %v", i+1, err)
		}
	}
	_ = srv
}

// TestBootstrapE2E_TOFUMismatchFails verifies that when known_hosts has
// a different key (T02.10 case 5) the second connect fails with a
// mismatch (MITM detection) — end-to-end through BootstrapProxmox.
func TestBootstrapE2E_TOFUMismatchFails(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}
	portNum, _ := strconv.Atoi(port)
	home := os.Getenv("ORCA_HOME")
	knownHostsPath := filepath.Join(home, "known_hosts")

	altPub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("ed25519 gen: %v", err)
	}
	altKey, err := ssh.NewPublicKey(altPub)
	if err != nil {
		t.Fatalf("new pub: %v", err)
	}
	addr := host + ":" + port
	altLine := knownhosts.Line([]string{knownhosts.Normalize(addr)}, altKey)
	if err := os.WriteFile(knownHostsPath, []byte(altLine+"\n"), 0o600); err != nil {
		t.Fatalf("write known_hosts: %v", err)
	}

	_, err = BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	})
	if err == nil {
		t.Fatal("expected MITM/mismatch error, got nil")
	}
	if !strings.Contains(err.Error(), "ssh dial") {
		t.Errorf("error should mention ssh dial, got: %v", err)
	}
}

// TestBootstrapE2E_PrePopulatedKnownHostsMatches verifies the v0.6→v0.8
// migration path (T02.10 case 7): a known_hosts entry written by a prior
// join (simulating a v0.6 install) is matched on second-connect without
// re-capture, end-to-end through BootstrapProxmox.
func TestBootstrapE2E_PrePopulatedKnownHostsMatches(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	hostKey := srv.hostPublicKey()
	if hostKey == nil {
		t.Fatal("server host key is nil")
	}
	portNum, _ := strconv.Atoi(port)
	home := os.Getenv("ORCA_HOME")
	knownHostsPath := filepath.Join(home, "known_hosts")

	addr := host + ":" + port
	preLine := knownhosts.Line([]string{knownhosts.Normalize(addr)}, hostKey)
	if err := os.WriteFile(knownHostsPath, []byte(preLine+"\n"), 0o600); err != nil {
		t.Fatalf("write known_hosts: %v", err)
	}

	result, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	})
	if err != nil {
		t.Fatalf("BootstrapProxmox on pre-populated known_hosts: %v", err)
	}
	expectedFP := security.SSHFingerprintSHA256(hostKey)
	if result.HostKeyFingerprint != expectedFP {
		t.Errorf("Result.HostKeyFingerprint = %q, want %q", result.HostKeyFingerprint, expectedFP)
	}
}

// TestBootstrapE2E_KeyResetThenRePin verifies T02.10 case 6: after
// ResetHostKey removes the known_hosts entry, the next BootstrapProxmox
// connect re-pins the key via TOFU and succeeds end-to-end. The reset
// target is the known_hosts entry key (host:port, normalized), which
// matches how the cli resolves the host from a proxmox node's address
// for non-default ports.
func TestBootstrapE2E_KeyResetThenRePin(t *testing.T) {
	srv, host, port := bootstrapE2ESetup(t)
	portNum, _ := strconv.Atoi(port)
	home := os.Getenv("ORCA_HOME")
	knownHostsPath := filepath.Join(home, "known_hosts")
	addr := host + ":" + port

	// First connect: TOFU captures + writes known_hosts.
	sessionRunner = nil
	if _, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	}); err != nil {
		t.Fatalf("first bootstrap: %v", err)
	}
	before, _ := os.ReadFile(knownHostsPath)
	if len(before) == 0 {
		t.Fatal("precondition: known_hosts empty after first connect")
	}

	// Reset: known_hosts entry removed. Pass the full addr (host:port)
	// so Normalize produces the same bracketed form the TOFU callback
	// wrote for a non-default port.
	if err := ResetHostKey(addr); err != nil {
		t.Fatalf("ResetHostKey: %v", err)
	}
	after, _ := os.ReadFile(knownHostsPath)
	if strings.Contains(string(after), knownhosts.Normalize(addr)) {
		t.Fatalf("known_hosts still contains host after reset: %q", after)
	}

	// Next connect re-pins via TOFU + succeeds.
	sessionRunner = nil
	if _, err := BootstrapProxmox(t.Context(), Options{
		Host:       host,
		SSHKeyPath: certpaths.SSHKeyPath(),
		SSHPort:    portNum,
	}); err != nil {
		t.Fatalf("re-pin bootstrap after reset: %v", err)
	}
	rePinned, _ := os.ReadFile(knownHostsPath)
	if !strings.Contains(string(rePinned), knownhosts.Normalize(addr)) {
		t.Fatalf("known_hosts not re-populated on next connect: %q", rePinned)
	}
	_ = srv
}
