package actuator

import (
	"bytes"
	"context"
	"crypto/ed25519"
	"crypto/rand"
	"encoding/pem"
	"os"
	"path/filepath"
	"strings"
	"testing"

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

func TestLoadSignerRejectsBadInput(t *testing.T) {
	if _, err := LoadSignerFromBytes([]byte("not a private key")); err == nil {
		t.Error("LoadSignerFromBytes should reject a non-key input")
	}
	if _, err := LoadSigner("/nonexistent/key"); err == nil {
		t.Error("LoadSigner should fail on a missing file")
	}
}

func TestLoadSignerRoundTrip(t *testing.T) {
	_, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("generate key: %v", err)
	}
	block, err := ssh.MarshalPrivateKey(priv, "")
	if err != nil {
		t.Fatalf("marshal private key: %v", err)
	}
	pemBytes := pem.EncodeToMemory(block)

	signer, err := LoadSignerFromBytes(pemBytes)
	if err != nil {
		t.Fatalf("LoadSignerFromBytes on a valid key: %v", err)
	}
	if signer == nil {
		t.Fatal("signer is nil")
	}

	dir := t.TempDir()
	path := filepath.Join(dir, "id_ed25519")
	if err := os.WriteFile(path, pemBytes, 0o600); err != nil {
		t.Fatalf("write key file: %v", err)
	}
	fromFile, err := LoadSigner(path)
	if err != nil {
		t.Fatalf("LoadSigner(%s): %v", path, err)
	}
	if !bytes.Equal(fromFile.PublicKey().Marshal(), signer.PublicKey().Marshal()) {
		t.Error("file and in-memory signers resolved to different public keys")
	}
}

// Dial needs a real SSH server to run a command, but its option normalization
// is verifiable without one: a zero Port must default to 22 (so the dial error
// references host:22, not host:0), and a closed port yields a dial error rather
// than panicking.
func TestDialDefaultsPort(t *testing.T) {
	_, err := Dial(context.Background(), DialOptions{Host: "127.0.0.1", Signer: mustSigner(t)})
	if err == nil {
		t.Fatal("Dial to a closed port should fail")
	}
	if !strings.Contains(err.Error(), "127.0.0.1:22") {
		t.Errorf("Dial error = %q, want it to reference 127.0.0.1:22", err)
	}
}

func mustSigner(t *testing.T) ssh.Signer {
	t.Helper()
	_, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("generate key: %v", err)
	}
	block, err := ssh.MarshalPrivateKey(priv, "")
	if err != nil {
		t.Fatalf("marshal key: %v", err)
	}
	s, err := LoadSignerFromBytes(pem.EncodeToMemory(block))
	if err != nil {
		t.Fatalf("parse key: %v", err)
	}
	return s
}
