package pbs

import (
	"bytes"
	"context"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// verifyServer is a fake PBS that answers verify POSTs, task status, and a snapshot list whose
// verification.state is controlled by `state` ("" → no verification field → none).
func verifyServer(t *testing.T, state string) (*httptest.Server, string) {
	t.Helper()
	verification := ""
	if state != "" {
		verification = `"verification":{"state":"` + state + `","upid":"UPID:dooplex:0:0:0:0:verify:ds:u:"},`
	}
	body := `{"data":[{"backup-type":"ct","backup-id":"9001","backup-time":1781014713,"size":10,"owner":"u",` +
		verification + `"files":[{"filename":"root.pxar.didx","crypt-mode":"encrypt","size":10}]}]}`
	return newPBSTestServer(t, func(w http.ResponseWriter, r *http.Request) {
		switch {
		case strings.Contains(r.URL.Path, "/verify"):
			w.Write([]byte(`{"data":"UPID:dooplex:0:0:0:0:verify:ds:u:"}`))
		case strings.Contains(r.URL.Path, "/status"):
			w.Write([]byte(`{"data":{"status":"stopped","exitstatus":"OK","node":"dooplex"}}`))
		default: // snapshots
			w.Write([]byte(body))
		}
	})
}

func TestVerifyLoop_RecordsState(t *testing.T) {
	ts, fp := verifyServer(t, "ok")
	host, port := hostPort(t, ts.URL)
	c, _ := NewClient(Config{Server: host, Port: port, Fingerprint: fp, TokenID: "u@pbs!t", Secret: "s"})
	store := NewSnapshotStore()
	loop := NewVerifyLoop(VerifyLoopOptions{
		Targets: func(context.Context) ([]Target, error) { return []Target{{Datastore: "ds", Client: c}}, nil },
		Store:   store,
	})
	loop.RunOnce(context.Background())

	snaps := store.PBSSnapshots(context.Background())
	if len(snaps) != 1 || snaps[0].VerifyState != VerifyOK {
		t.Fatalf("loop should record 1 ok snapshot, got %+v", snaps)
	}
}

func TestVerifyLoop_FailedVerifyRecorded(t *testing.T) {
	ts, fp := verifyServer(t, "failed")
	host, port := hostPort(t, ts.URL)
	c, _ := NewClient(Config{Server: host, Port: port, Fingerprint: fp, TokenID: "u@pbs!t", Secret: "s"})
	store := NewSnapshotStore()
	NewVerifyLoop(VerifyLoopOptions{
		Targets: func(context.Context) ([]Target, error) { return []Target{{Datastore: "ds", Client: c}}, nil },
		Store:   store,
	}).RunOnce(context.Background())
	if s := store.PBSSnapshots(context.Background()); len(s) != 1 || s[0].VerifyState != VerifyFailed {
		t.Fatalf("failed verify must be recorded, got %+v", s)
	}
}

// TestVerifyLoop_NeverLogsSecret runs a full cycle with the loop's logger capturing output and
// asserts the token secret never appears in any log line.
func TestVerifyLoop_NeverLogsSecret(t *testing.T) {
	const secret = "tok-secret-DO-NOT-LOG-7f3a"
	ts, fp := verifyServer(t, "ok")
	host, port := hostPort(t, ts.URL)
	c, _ := NewClient(Config{Server: host, Port: port, Fingerprint: fp, TokenID: "felhom@pbs!n100", Secret: secret})

	var buf bytes.Buffer
	logger := slog.New(slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug}))
	loop := NewVerifyLoop(VerifyLoopOptions{
		Targets: func(context.Context) ([]Target, error) { return []Target{{Datastore: "ds", Client: c}}, nil },
		Store:   NewSnapshotStore(),
		Logger:  logger,
	})
	loop.RunOnce(context.Background())
	if strings.Contains(buf.String(), secret) {
		t.Fatalf("the verify loop logged the token secret")
	}
}

func TestVerifyLoop_DisabledByNegativeCadence(t *testing.T) {
	loop := NewVerifyLoop(VerifyLoopOptions{
		Targets: func(context.Context) ([]Target, error) { return nil, nil },
		Store:   NewSnapshotStore(),
		Cadence: -1,
	})
	ctx, cancel := context.WithCancel(context.Background())
	done := make(chan error, 1)
	go func() { done <- loop.Run(ctx) }()
	cancel()
	if err := <-done; err != nil {
		t.Fatalf("disabled loop Run should return nil on cancel, got %v", err)
	}
}
