package localapi

import (
	"bytes"
	"encoding/json"
	"log/slog"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

// TestStageEscrowSecret_StagesScopesAndHidesValue: the pushed restic password is staged 0600, the ack is
// non-secret, a cross-guest push is refused, and the value NEVER appears in a log line.
func TestStageEscrowSecret_StagesScopesAndHidesValue(t *testing.T) {
	srv := newTestServerS(t, &fakeGuests{}, &fakeBackups{}, &fakeStore{}, nil)
	stagePath := filepath.Join(t.TempDir(), "escrow-stage", "restic_repo_password")
	srv.escrowStagePath = stagePath
	h := srv.Handler()

	const secret = "d78466fcbf595b488ba8b962fcc521f68fbe6f36b8b8e57815a6124bccf4526b"

	// happy path: token A → vmid 8200
	w := do(t, h, "POST", "/escrow/stage-secret", "A", `{"vmid":8200,"restic_repo_password":"`+secret+`"}`)
	if w.Code != 200 {
		t.Fatalf("stage: got %d body=%s", w.Code, w.Body.String())
	}
	// EFFECT: the 0600 file holds the secret byte-exact
	got, err := os.ReadFile(stagePath)
	if err != nil {
		t.Fatalf("staged file missing: %v", err)
	}
	if string(got) != secret {
		t.Fatal("staged file content does not match the pushed secret")
	}
	if fi, _ := os.Stat(stagePath); os.PathSeparator == '/' && fi.Mode().Perm() != 0o600 {
		t.Errorf("stage file must be 0600, got %v", fi.Mode().Perm())
	}
	// ack is non-secret {ok, data:{staged:true}} and does NOT echo the value
	if strings.Contains(w.Body.String(), secret) {
		t.Fatal("the response echoed the secret value")
	}
	var ack struct {
		OK   bool `json:"ok"`
		Data struct {
			Staged bool `json:"staged"`
		} `json:"data"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &ack); err != nil || !ack.OK || !ack.Data.Staged {
		t.Fatalf("want {ok,data.staged:true}, got %s", w.Body.String())
	}

	// cross-guest: token A (8200) claiming vmid 9300 → 403, and the staged secret is NOT overwritten
	w2 := do(t, h, "POST", "/escrow/stage-secret", "A", `{"vmid":9300,"restic_repo_password":"attacker"}`)
	if w2.Code != 403 {
		t.Fatalf("cross-guest push must be 403, got %d", w2.Code)
	}
	if got2, _ := os.ReadFile(stagePath); string(got2) != secret {
		t.Fatal("cross-guest push must not overwrite the staged secret")
	}

	// empty password → 400
	if w3 := do(t, h, "POST", "/escrow/stage-secret", "A", `{"vmid":8200,"restic_repo_password":""}`); w3.Code != 400 {
		t.Fatalf("empty password must be 400, got %d", w3.Code)
	}
}

// TestWipeStagedEscrowSecret: DELETE removes the staged file (EFFECT: file gone), is idempotent when
// absent, and requires auth.
func TestWipeStagedEscrowSecret(t *testing.T) {
	srv := newTestServerS(t, &fakeGuests{}, &fakeBackups{}, &fakeStore{}, nil)
	stagePath := filepath.Join(t.TempDir(), "escrow-stage", "restic_repo_password")
	srv.escrowStagePath = stagePath
	h := srv.Handler()

	// stage something first
	if w := do(t, h, "POST", "/escrow/stage-secret", "A", `{"vmid":8200,"restic_repo_password":"deadbeef"}`); w.Code != 200 {
		t.Fatalf("stage: got %d", w.Code)
	}
	if _, err := os.Stat(stagePath); err != nil {
		t.Fatalf("staged file must exist before the wipe: %v", err)
	}

	// wipe → removed:true, file gone
	w := do(t, h, "DELETE", "/escrow/stage-secret", "A", "")
	if w.Code != 200 || !strings.Contains(w.Body.String(), `"removed":true`) {
		t.Fatalf("wipe: got %d body=%s", w.Code, w.Body.String())
	}
	if _, err := os.Stat(stagePath); !os.IsNotExist(err) {
		t.Fatal("staged file must be GONE after the wipe")
	}

	// idempotent: wipe again → 200 removed:false
	w2 := do(t, h, "DELETE", "/escrow/stage-secret", "A", "")
	if w2.Code != 200 || !strings.Contains(w2.Body.String(), `"removed":false`) {
		t.Fatalf("re-wipe must be a clean idempotent 200 removed:false, got %d body=%s", w2.Code, w2.Body.String())
	}

	// auth required
	if w3 := do(t, h, "DELETE", "/escrow/stage-secret", "NOPE", ""); w3.Code != 401 {
		t.Fatalf("unauthenticated wipe must be 401, got %d", w3.Code)
	}
}

// TestStageEscrowSecret_NoSecretInLog: the staged value never appears in a log line (hygiene is load-bearing).
func TestStageEscrowSecret_NoSecretInLog(t *testing.T) {
	var logbuf bytes.Buffer
	srv, err := NewServer(Options{
		ListenAddr:      "127.0.0.1:0",
		Guests:          &fakeGuests{},
		Backups:         &fakeBackups{},
		Store:           &fakeStore{},
		Storage:         fakeStorage{},
		Tokens:          staticTokens{"A": 8200},
		BackupCadence:   time.Hour,
		EscrowStagePath: filepath.Join(t.TempDir(), "s", "pw"),
		Logger:          slog.New(slog.NewTextHandler(&logbuf, nil)),
	})
	if err != nil {
		t.Fatal(err)
	}
	const secret = "cafef00ddeadbeef0123456789abcdef0123456789abcdef0123456789abcdef"
	do(t, srv.Handler(), "POST", "/escrow/stage-secret", "A", `{"vmid":8200,"restic_repo_password":"`+secret+`"}`)
	if strings.Contains(logbuf.String(), secret) {
		t.Fatalf("the staged secret value leaked into a log line")
	}
}
