//go:build !windows

package updater

import (
	"bytes"
	"io"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"runtime"
	"testing"
)

// TestUpdate_LinuxAtomicSwap stages a fake "running binary" file, runs
// UpdateForTest against a fake /agent/binary server, and asserts that
// the binary was swapped, .old preserves the previous bytes, and .new
// was renamed away.
func TestUpdate_LinuxAtomicSwap(t *testing.T) {
	tmp := t.TempDir()
	binPath := filepath.Join(tmp, "agent")
	if err := os.WriteFile(binPath, []byte("OLD"), 0o755); err != nil {
		t.Fatal(err)
	}
	newBytes := []byte("NEW BINARY CONTENTS")

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/agent/binary" {
			http.NotFound(w, r)
			return
		}
		gotOS, gotArch := r.URL.Query().Get("os"), r.URL.Query().Get("arch")
		if gotOS != runtime.GOOS || gotArch != runtime.GOARCH {
			t.Errorf("query mismatch: got os=%s arch=%s want %s/%s",
				gotOS, gotArch, runtime.GOOS, runtime.GOARCH)
		}
		_, _ = io.Copy(w, bytes.NewReader(newBytes))
	}))
	defer srv.Close()

	if err := UpdateForTest(srv.URL, binPath); err != nil {
		t.Fatalf("update: %v", err)
	}

	got, err := os.ReadFile(binPath)
	if err != nil {
		t.Fatal(err)
	}
	if string(got) != string(newBytes) {
		t.Fatalf("binary contents: got %q want %q", got, newBytes)
	}
	old, err := os.ReadFile(binPath + ".old")
	if err != nil {
		t.Fatalf("agent.old missing: %v", err)
	}
	if string(old) != "OLD" {
		t.Fatalf("agent.old contents: got %q want %q", old, "OLD")
	}
	if _, err := os.Stat(binPath + ".new"); !os.IsNotExist(err) {
		t.Fatalf("agent.new should be absent after swap, got err=%v", err)
	}
}

// TestUpdate_FetchHTTPError surfaces the server's status when the
// binary is not published for this os/arch.
func TestUpdate_FetchHTTPError(t *testing.T) {
	tmp := t.TempDir()
	binPath := filepath.Join(tmp, "agent")
	if err := os.WriteFile(binPath, []byte("OLD"), 0o755); err != nil {
		t.Fatal(err)
	}

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, `{"error":"binary_not_published"}`, http.StatusNotFound)
	}))
	defer srv.Close()

	err := UpdateForTest(srv.URL, binPath)
	if err == nil {
		t.Fatal("expected error, got nil")
	}
	got, _ := os.ReadFile(binPath)
	if string(got) != "OLD" {
		t.Fatalf("binary should not have changed, got %q", got)
	}
}
