package guest

import (
	"archive/tar"
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io"
	"os"
	"path/filepath"
	"testing"

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

func TestWriteTarArchiveKeepsTopLevelDirectory(t *testing.T) {
	t.Parallel()

	sourceDir := filepath.Join(t.TempDir(), "6.8.0-test")
	if err := os.MkdirAll(filepath.Join(sourceDir, "kernel"), 0o755); err != nil {
		t.Fatalf("MkdirAll: %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "modules.dep"), []byte("deps"), 0o644); err != nil {
		t.Fatalf("WriteFile modules.dep: %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "kernel", "module.ko"), []byte("ko"), 0o644); err != nil {
		t.Fatalf("WriteFile module.ko: %v", err)
	}

	var buf bytes.Buffer
	if err := writeTarArchive(&buf, sourceDir); err != nil {
		t.Fatalf("writeTarArchive: %v", err)
	}

	tr := tar.NewReader(bytes.NewReader(buf.Bytes()))
	var names []string
	for {
		header, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Fatalf("tar.Next: %v", err)
		}
		names = append(names, header.Name)
	}

	want := map[string]struct{}{
		"6.8.0-test":                  {},
		"6.8.0-test/modules.dep":      {},
		"6.8.0-test/kernel":           {},
		"6.8.0-test/kernel/module.ko": {},
	}
	if len(names) != len(want) {
		t.Fatalf("archive names = %v, want %d entries", names, len(want))
	}
	for _, name := range names {
		if _, ok := want[name]; !ok {
			t.Fatalf("unexpected archive entry %q in %v", name, names)
		}
	}
}

func TestAuthorizedPublicKey(t *testing.T) {
	t.Parallel()

	privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		t.Fatalf("GenerateKey: %v", err)
	}
	privateKeyPEM := pem.EncodeToMemory(&pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
	})
	keyPath := filepath.Join(t.TempDir(), "id_rsa")
	if err := os.WriteFile(keyPath, privateKeyPEM, 0o600); err != nil {
		t.Fatalf("WriteFile: %v", err)
	}

	publicKey, err := AuthorizedPublicKey(keyPath)
	if err != nil {
		t.Fatalf("AuthorizedPublicKey: %v", err)
	}
	parsed, _, _, _, err := ssh.ParseAuthorizedKey(publicKey)
	if err != nil {
		t.Fatalf("ParseAuthorizedKey: %v", err)
	}
	if parsed.Type() != ssh.KeyAlgoRSA {
		t.Fatalf("key type = %q, want %q", parsed.Type(), ssh.KeyAlgoRSA)
	}
}

func TestWriteTarEntriesArchiveIncludesOnlySelectedPaths(t *testing.T) {
	t.Parallel()

	sourceDir := filepath.Join(t.TempDir(), "repo")
	if err := os.MkdirAll(filepath.Join(sourceDir, "nested"), 0o755); err != nil {
		t.Fatalf("MkdirAll(nested): %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "tracked.txt"), []byte("tracked"), 0o644); err != nil {
		t.Fatalf("WriteFile(tracked.txt): %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "nested", "keep.txt"), []byte("keep"), 0o644); err != nil {
		t.Fatalf("WriteFile(keep.txt): %v", err)
	}
	if err := os.WriteFile(filepath.Join(sourceDir, "nested", "skip.txt"), []byte("skip"), 0o644); err != nil {
		t.Fatalf("WriteFile(skip.txt): %v", err)
	}

	var buf bytes.Buffer
	if err := writeTarEntriesArchive(&buf, sourceDir, []string{"tracked.txt", "nested/keep.txt"}); err != nil {
		t.Fatalf("writeTarEntriesArchive: %v", err)
	}

	tr := tar.NewReader(bytes.NewReader(buf.Bytes()))
	var names []string
	for {
		header, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			t.Fatalf("tar.Next: %v", err)
		}
		names = append(names, header.Name)
	}

	want := map[string]struct{}{
		"repo/tracked.txt":     {},
		"repo/nested/keep.txt": {},
	}
	if len(names) != len(want) {
		t.Fatalf("archive names = %v, want %d entries", names, len(want))
	}
	for _, name := range names {
		if _, ok := want[name]; !ok {
			t.Fatalf("unexpected archive entry %q in %v", name, names)
		}
	}
}
