package security

import (
	"crypto/ed25519"
	"crypto/rand"
	"os"
	"path/filepath"
	"strings"
	"testing"

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

func TestGenerateOrLoadSSHKey_Generates(t *testing.T) {
	dir := t.TempDir()

	keyPEM, pubLine, err := GenerateOrLoadSSHKey(dir)
	if err != nil {
		t.Fatalf("generate: %v", err)
	}

	// Private key file exists with mode 0600.
	keyPath := filepath.Join(dir, sshKeyFile)
	info, err := os.Stat(keyPath)
	if err != nil {
		t.Fatalf("stat key: %v", err)
	}
	if info.Mode().Perm() != SSHKeyMode {
		t.Errorf("key mode = %04o, want %04o", info.Mode().Perm(), SSHKeyMode)
	}

	// Public key file exists with mode 0644.
	pubPath := filepath.Join(dir, sshPubFile)
	info, err = os.Stat(pubPath)
	if err != nil {
		t.Fatalf("stat pub: %v", err)
	}
	if info.Mode().Perm() != SSHPubMode {
		t.Errorf("pub mode = %04o, want %04o", info.Mode().Perm(), SSHPubMode)
	}

	// Public key line is ssh-ed25519 format.
	if !strings.HasPrefix(string(pubLine), "ssh-ed25519 ") {
		t.Errorf("pub line = %q, want ssh-ed25519 prefix", string(pubLine))
	}

	// Private key PEM parses with ssh.ParsePrivateKey (PKCS8).
	signer, err := ssh.ParsePrivateKey(keyPEM)
	if err != nil {
		t.Fatalf("parse private key: %v", err)
	}
	if signer.PublicKey().Type() != "ssh-ed25519" {
		t.Errorf("signer key type = %q, want ssh-ed25519", signer.PublicKey().Type())
	}
}

func TestGenerateOrLoadSSHKey_IdempotentLoad(t *testing.T) {
	dir := t.TempDir()

	// First call generates.
	keyPEM1, pubLine1, err := GenerateOrLoadSSHKey(dir)
	if err != nil {
		t.Fatalf("first generate: %v", err)
	}

	// Second call loads existing.
	keyPEM2, pubLine2, err := GenerateOrLoadSSHKey(dir)
	if err != nil {
		t.Fatalf("second load: %v", err)
	}

	if string(keyPEM1) != string(keyPEM2) {
		t.Error("key was regenerated on second call (D-036 idempotency violation)")
	}
	if string(pubLine1) != string(pubLine2) {
		t.Error("pub was regenerated on second call (D-036 idempotency violation)")
	}
}

func TestGenerateOrLoadSSHKey_EmptyDir(t *testing.T) {
	_, _, err := GenerateOrLoadSSHKey("")
	if err == nil {
		t.Error("expected error for empty dir")
	}
}

func TestGenerateOrLoadSSHKey_CreatesDir(t *testing.T) {
	dir := filepath.Join(t.TempDir(), "nested", "ssh-dir")
	if _, _, err := GenerateOrLoadSSHKey(dir); err != nil {
		t.Fatalf("generate with nested dir: %v", err)
	}
	if _, err := os.Stat(dir); err != nil {
		t.Errorf("nested dir not created: %v", err)
	}
}

func TestSSHFingerprintSHA256_Ed25519(t *testing.T) {
	pub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("ed25519 gen: %v", err)
	}
	sshPub, err := ssh.NewPublicKey(pub)
	if err != nil {
		t.Fatalf("new pubkey: %v", err)
	}

	got := SSHFingerprintSHA256(sshPub)

	// Canonical form: SHA256: followed by unpadded base64.
	if !strings.HasPrefix(got, "SHA256:") {
		t.Fatalf("fingerprint = %q, want SHA256: prefix", got)
	}
	// Must match the reference implementation exactly.
	want := ssh.FingerprintSHA256(sshPub)
	if got != want {
		t.Errorf("SSHFingerprintSHA256 = %q, want %q", got, want)
	}
}

func TestSSHFingerprintSHA256_StableAcrossCalls(t *testing.T) {
	pub, _, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		t.Fatalf("ed25519 gen: %v", err)
	}
	sshPub, err := ssh.NewPublicKey(pub)
	if err != nil {
		t.Fatalf("new pubkey: %v", err)
	}

	a := SSHFingerprintSHA256(sshPub)
	b := SSHFingerprintSHA256(sshPub)
	if a != b {
		t.Errorf("fingerprint not stable: %q vs %q", a, b)
	}
}
