package web

// PBS DR SLICE 1 — the provisioning flow behind the config form, against a FAKE tenancy
// provisioner (no SSH in CI). The load-bearing contracts: descriptor lands in the HOST
// desired_json + generation bump; the secret is stored consume-once and appears NOWHERE else
// (ConfigJSON, desired-state, logs); every failure is fail-closed (no descriptor, no bump, no
// secret); an already-provisioned re-save is a pure no-op (no re-key, no second secret, no
// spurious bump); re-issue rotates the secret + bumps.

import (
	"bytes"
	"context"
	"database/sql"
	"errors"
	"log"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"
	"time"

	"gitea.dooplex.hu/admin/felhom-hub/internal/store"
	"gitea.dooplex.hu/admin/felhom-hub/internal/tenantsync"
)

type fakeTenancy struct {
	provisionCalls     int
	reissueCalls       int
	deprovisionCalls   int
	err                error // both ops fail with this
	provisionErr       error // Provision-only failure (the F-14 token_exists shape: reissue still works)
	deprovisionErr     error // Deprovision-only failure (RESET partial-failure red-proof)
	deprovisionExisted bool  // what Deprovision reports (namespace existed / was destroyed)
	secret             string
}

func (f *fakeTenancy) result(customerID string) *tenantsync.Result {
	return &tenantsync.Result{
		TokenID:     "felhom@pbs!" + customerID,
		TokenSecret: f.secret,
		Fingerprint: "aa:bb:cc",
		Datastore:   "felhom-offsite",
		Namespace:   customerID,
	}
}

func (f *fakeTenancy) Provision(ctx context.Context, customerID string) (*tenantsync.Result, error) {
	f.provisionCalls++
	if f.err != nil {
		return nil, f.err
	}
	if f.provisionErr != nil {
		return nil, f.provisionErr
	}
	return f.result(customerID), nil
}

func (f *fakeTenancy) Reissue(ctx context.Context, customerID string) (*tenantsync.Result, error) {
	f.reissueCalls++
	if f.err != nil {
		return nil, f.err
	}
	return f.result(customerID), nil
}

func (f *fakeTenancy) Deprovision(ctx context.Context, customerID string) (bool, error) {
	f.deprovisionCalls++
	if f.deprovisionErr != nil {
		return false, f.deprovisionErr
	}
	if f.err != nil {
		return false, f.err
	}
	return f.deprovisionExisted, nil
}

// newPBSDRServer builds a server + store with the full provisioning preconditions satisfied:
// customer config, enrolled host, WG endpoint record, bound WG peer. The logger is captured so
// tests can grep-assert the secret never reaches it.
func newPBSDRServer(t *testing.T, fake *fakeTenancy) (*Server, *store.Store, *bytes.Buffer) {
	t.Helper()
	s, st := newTestServer(t)
	logBuf := &bytes.Buffer{}
	s.logger = log.New(logBuf, "", 0)
	if fake != nil {
		s.SetTenantSync(fake)
	}
	if err := st.SaveCustomerConfig(&store.CustomerConfig{
		CustomerID: "peti", APIKey: "capi", RetrievalPassword: "pw",
	}); err != nil {
		t.Fatalf("seed config: %v", err)
	}
	if err := st.UpsertHost(&store.Host{HostID: "peti-01", CustomerID: "peti", APIKey: "hapi"}); err != nil {
		t.Fatalf("seed host: %v", err)
	}
	if err := st.SetWGEndpoint(&store.WGEndpoint{
		EndpointID: "ep0", DNSName: "ep0.felhom.eu", WGPort: 443,
		ServerPubkey: "SPK", TunnelSubnet: "10.77.0.0/24", PBSTunnelIP: "10.77.0.1",
	}); err != nil {
		t.Fatalf("seed endpoint: %v", err)
	}
	if _, _, err := st.RegisterWGPeerForHost("peti-01", "PETIPUBKEY"); err != nil {
		t.Fatalf("seed wg peer: %v", err)
	}
	return s, st, logBuf
}

// postUpdate drives the REAL handler pipeline (handleConfigUpdate → applyPBSDR → save).
func postUpdate(t *testing.T, s *Server, form url.Values) *httptest.ResponseRecorder {
	t.Helper()
	// v0.48.0 edit-a: the update handler now enforces the form's required fields server-side —
	// supply them like every real submission does (the browser form marks both `required`).
	if form.Get("customer_name") == "" {
		form.Set("customer_name", "Peti")
	}
	if form.Get("domain") == "" {
		form.Set("domain", "peti.example")
	}
	req := httptest.NewRequest("POST", "/configs/peti", strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	rr := httptest.NewRecorder()
	s.handleConfigUpdate(rr, req, "peti")
	return rr
}

func hostState(t *testing.T, st *store.Store) (desc *pbsDRDescriptor, desiredJSON string, gen int64) {
	t.Helper()
	h, err := st.GetHost("peti-01")
	if err != nil || h == nil {
		t.Fatalf("host read: %v", err)
	}
	return readPBSDR(h.DesiredJSON), h.DesiredJSON, h.DesiredGeneration
}

func TestPBSDR_ProvisionHappyPath(t *testing.T) {
	fake := &fakeTenancy{secret: "SUPER-SECRET-TOKEN"}
	s, st, logBuf := newPBSDRServer(t, fake)

	rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}, "pbsdr_storage_id": {"felhom-pbs"}})
	if rr.Code != 303 {
		t.Fatalf("save = %d (%s), want 303", rr.Code, rr.Body.String())
	}
	if fake.provisionCalls != 1 {
		t.Errorf("provision calls = %d, want 1", fake.provisionCalls)
	}

	desc, desiredJSON, gen := hostState(t, st)
	if desc == nil {
		t.Fatalf("no pbs_dr descriptor in desired_json: %s", desiredJSON)
	}
	if !desc.Enabled || desc.StorageID != "felhom-pbs" || desc.PBSTunnelIP != "10.77.0.1" ||
		desc.Datastore != "felhom-offsite" || desc.Namespace != "peti" ||
		desc.TokenID != "felhom@pbs!peti" || desc.Fingerprint != "aa:bb:cc" {
		t.Errorf("descriptor wrong: %+v", desc)
	}
	if gen != 1 {
		t.Errorf("desired_generation = %d, want 1 (exactly one bump)", gen)
	}

	// The secret: stored consume-once for the HOST…
	got, err := st.ConsumeHostPBSSecret("peti-01")
	if err != nil || got != "SUPER-SECRET-TOKEN" {
		t.Fatalf("consume = (%q, %v), want the stored secret", got, err)
	}
	// …and NOWHERE else: not in the desired-state, not in ConfigJSON, not in any log line.
	if strings.Contains(desiredJSON, "SUPER-SECRET-TOKEN") {
		t.Error("secret leaked into desired_json")
	}
	cfg, _ := st.GetCustomerConfig("peti")
	if strings.Contains(cfg.ConfigJSON, "SUPER-SECRET-TOKEN") || strings.Contains(cfg.ConfigJSON, "pbs_dr") {
		t.Errorf("ConfigJSON must carry neither the secret nor the descriptor: %s", cfg.ConfigJSON)
	}
	if strings.Contains(logBuf.String(), "SUPER-SECRET-TOKEN") {
		t.Error("secret leaked into the hub log")
	}
}

func TestPBSDR_ResaveIsNoOp(t *testing.T) {
	fake := &fakeTenancy{secret: "SUPER-SECRET-TOKEN"}
	s, st, _ := newPBSDRServer(t, fake)

	postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	if _, err := st.ConsumeHostPBSSecret("peti-01"); err != nil {
		t.Fatalf("first secret consume: %v", err)
	}

	// Idempotent re-save: NO re-key, NO second secret row, NO spurious generation bump.
	// (Red-proof: dropping the already-provisioned short-circuit in applyPBSDR re-runs
	// Provision → calls=2 + a fresh consumable secret → FAIL.)
	rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	if rr.Code != 303 {
		t.Fatalf("re-save = %d, want 303 (already-provisioned is success-no-op)", rr.Code)
	}
	if fake.provisionCalls != 1 {
		t.Errorf("provision calls after re-save = %d, want 1 (token must not rotate)", fake.provisionCalls)
	}
	if _, err := st.ConsumeHostPBSSecret("peti-01"); err != sql.ErrNoRows {
		t.Errorf("re-save created a fresh secret (consume err %v, want ErrNoRows)", err)
	}
	if _, _, gen := hostState(t, st); gen != 1 {
		t.Errorf("generation after re-save = %d, want 1 (no spurious bump)", gen)
	}
}

// v0.51.0 (DR-tier-by-default): an UNMET PRECONDITION is an honest waiting stage, not a
// save-blocking error — the flag is stored and the cascade converges later. A REAL provisioning
// failure stays fail-closed. Red-proof partner for the coupling: drop the WG-peer stage check in
// pbsdrProvisionAtom → "no WG peer waits" fails (provision would be reached).
func TestPBSDR_FailClosed(t *testing.T) {
	t.Run("no WG peer waits (stage, not error)", func(t *testing.T) {
		fake := &fakeTenancy{secret: "S"}
		s, st, logBuf := newPBSDRServer(t, fake)
		// Kill the precondition: the host has never registered a WG key.
		if err := st.RemoveWGPeer("PETIPUBKEY"); err != nil {
			t.Fatalf("remove seed peer: %v", err)
		}
		rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
		if rr.Code != 303 {
			t.Fatalf("save without WG peer = %d (%s), want 303 — the flag stores, the cascade waits", rr.Code, rr.Body.String())
		}
		if fake.provisionCalls != 0 {
			t.Errorf("provision reached despite missing peer (%d calls)", fake.provisionCalls)
		}
		h, _ := st.GetHost("peti-01")
		if readPBSDR(h.DesiredJSON) != nil || h.DesiredGeneration != 0 {
			t.Errorf("descriptor written while waiting: desc=%v gen=%d", readPBSDR(h.DesiredJSON), h.DesiredGeneration)
		}
		// The flag itself IS saved (the intent survives the wait).
		cfg, _ := st.GetCustomerConfig("peti")
		if !cfg.DRTier {
			t.Error("DR flag not stored while the cascade waits")
		}
		if !strings.Contains(logBuf.String(), "has not reported a WG key yet") {
			t.Error("the waiting stage (guard wording) was not logged")
		}
	})

	t.Run("tenantsync error", func(t *testing.T) {
		fake := &fakeTenancy{err: errors.New("ssh boom")}
		s, st, _ := newPBSDRServer(t, fake)
		rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
		if rr.Code != 502 {
			t.Fatalf("save with tenantsync error = %d, want 502", rr.Code)
		}
		desc, _, gen := hostState(t, st)
		if desc != nil || gen != 0 {
			t.Errorf("fail-closed violated: desc=%+v gen=%d", desc, gen)
		}
		if _, err := st.ConsumeHostPBSSecret("peti-01"); err != sql.ErrNoRows {
			t.Errorf("a secret exists after a failed provision (err %v)", err)
		}
		// Fail-closed also means the config save itself was aborted.
		cfg, _ := st.GetCustomerConfig("peti")
		if cfg.CustomerName == "half-saved" {
			t.Error("config row saved despite provisioning failure")
		}
	})

	t.Run("token_exists points at re-issue", func(t *testing.T) {
		fake := &fakeTenancy{err: tenantsync.ErrTokenExists}
		s, st, _ := newPBSDRServer(t, fake)
		rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
		if rr.Code != 502 || !strings.Contains(rr.Body.String(), "Re-issue") {
			t.Fatalf("token_exists = %d (%s), want 502 mentioning Re-issue", rr.Code, rr.Body.String())
		}
		if desc, _, gen := hostState(t, st); desc != nil || gen != 0 {
			t.Errorf("state written on token_exists: desc=%+v gen=%d", desc, gen)
		}
		_ = st
	})

	t.Run("not configured waits (stage, not error)", func(t *testing.T) {
		s, st, _ := newPBSDRServer(t, nil) // no tenantsync
		rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
		if rr.Code != 303 {
			t.Fatalf("save without tenantsync = %d, want 303 — the flag stores, provisioning waits", rr.Code)
		}
		if desc, _, gen := hostState(t, st); desc != nil || gen != 0 {
			t.Errorf("state written without a provisioner: desc=%+v gen=%d", desc, gen)
		}
	})
}

// Scenario A (hands-free cascade): flag ON while the WG peer is missing → the save waits; the
// host's WG registration then fires PBSDRAutoProvision and the descriptor lands with ZERO
// operator steps. Red-proof partner: unhook the atom from PBSDRAutoProvision (early return) →
// this fails at "descriptor after WG registration".
func TestPBSDR_AutoProvisionOnWGRegistration(t *testing.T) {
	fake := &fakeTenancy{secret: "AUTO-SECRET"}
	s, st, logBuf := newPBSDRServer(t, fake)
	if err := st.RemoveWGPeer("PETIPUBKEY"); err != nil {
		t.Fatalf("remove seed peer: %v", err)
	}

	// 1. Flag ON, peer missing → stored intent, no descriptor.
	postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	if desc, _, _ := hostState(t, st); desc != nil {
		t.Fatalf("descriptor exists before the WG peer: %+v", desc)
	}

	// 2. The agent registers its WG key (what the api handler does) → the hook fires.
	if _, _, err := st.RegisterWGPeerForHost("peti-01", "PETIPUBKEY"); err != nil {
		t.Fatalf("register peer: %v", err)
	}
	s.PBSDRAutoProvision(context.Background(), "peti")

	desc, desiredJSON, _ := hostState(t, st)
	if desc == nil || !desc.Enabled || desc.Namespace != "peti" {
		t.Fatalf("descriptor after WG registration = %+v (json %s), want provisioned", desc, desiredJSON)
	}
	if fake.provisionCalls != 1 {
		t.Errorf("provision calls = %d, want 1", fake.provisionCalls)
	}
	if got, err := st.ConsumeHostPBSSecret("peti-01"); err != nil || got != "AUTO-SECRET" {
		t.Fatalf("consume-once secret after auto-provision = (%q, %v)", got, err)
	}
	if strings.Contains(logBuf.String(), "AUTO-SECRET") {
		t.Error("secret leaked into the hub log")
	}

	// 3. Idempotent: a second hook firing (re-registration) must not re-key.
	s.PBSDRAutoProvision(context.Background(), "peti")
	if fake.provisionCalls != 1 {
		t.Errorf("second hook firing re-provisioned (%d calls)", fake.provisionCalls)
	}

	// 4. Flag OFF → the hook never provisions (scenario B: off is off).
	fake2 := &fakeTenancy{secret: "S2"}
	s2, st2, _ := newPBSDRServer(t, fake2)
	cfg, _ := st2.GetCustomerConfig("peti")
	cfg.DRTier = false
	if err := st2.SaveCustomerConfig(cfg); err != nil {
		t.Fatalf("save flag-off config: %v", err)
	}
	s2.PBSDRAutoProvision(context.Background(), "peti")
	if fake2.provisionCalls != 0 {
		t.Errorf("hook provisioned with the DR flag OFF (%d calls)", fake2.provisionCalls)
	}
}

func TestPBSDR_DisableKeepsTenancy(t *testing.T) {
	fake := &fakeTenancy{secret: "S"}
	s, st, _ := newPBSDRServer(t, fake)
	postUpdate(t, s, url.Values{"dr_tier": {"on"}})

	// Unchecked box → descriptor enabled:false, coords kept, ONE bump; no endpoint mutation.
	rr := postUpdate(t, s, url.Values{})
	if rr.Code != 303 {
		t.Fatalf("disable save = %d, want 303", rr.Code)
	}
	desc, _, gen := hostState(t, st)
	if desc == nil || desc.Enabled || desc.Namespace != "peti" || desc.TokenID == "" {
		t.Fatalf("disable must keep coords with enabled=false: %+v", desc)
	}
	if gen != 2 {
		t.Errorf("generation = %d, want 2 (enable + disable)", gen)
	}
	// A second disabled save is a pure no-op.
	postUpdate(t, s, url.Values{})
	if _, _, gen := hostState(t, st); gen != 2 {
		t.Errorf("second disabled save bumped generation to %d", gen)
	}
	if fake.provisionCalls != 1 || fake.reissueCalls != 0 {
		t.Errorf("endpoint touched on disable: provision=%d reissue=%d", fake.provisionCalls, fake.reissueCalls)
	}
}

func TestPBSDR_StorageIDChangeUpdatesDescriptorOnly(t *testing.T) {
	fake := &fakeTenancy{secret: "S"}
	s, st, _ := newPBSDRServer(t, fake)
	postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	st.ConsumeHostPBSSecret("peti-01") // spend the provision secret

	rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}, "pbsdr_storage_id": {"felhom-offsite"}})
	if rr.Code != 303 {
		t.Fatalf("storage-id change = %d, want 303", rr.Code)
	}
	desc, _, gen := hostState(t, st)
	if desc.StorageID != "felhom-offsite" || !desc.Enabled {
		t.Errorf("descriptor not updated: %+v", desc)
	}
	if gen != 2 {
		t.Errorf("generation = %d, want 2", gen)
	}
	if fake.provisionCalls != 1 {
		t.Errorf("a descriptor edit re-provisioned the tenancy (%d calls)", fake.provisionCalls)
	}
	if _, err := st.ConsumeHostPBSSecret("peti-01"); err != sql.ErrNoRows {
		t.Errorf("a descriptor edit staged a new secret (err %v)", err)
	}
}

// F-14 scenario A (v0.53.0, operator ruling 2026-07-13): a tenancy orphaned by an ESCROW-ACKED
// host delete → the enable path auto-re-issues via the EXISTING tenantsync re-issue op, writes
// the audit event, and provisioning proceeds to a full descriptor + consume-once secret.
// RED-PROOF (Part 1): dropping the provenance write from DeleteHost's tx → the gate finds no
// record → this test fails with the 502 refusal.
func TestPBSDR_F14AutoReissueOnAckedDeletion(t *testing.T) {
	fake := &fakeTenancy{provisionErr: tenantsync.ErrTokenExists, secret: "REISSUED-SECRET"}
	s, st, logBuf := newPBSDRServer(t, fake)

	// The F-14 history: the tenancy's owning host was deleted through the escrow-ack flow
	// (real DeleteHost, real escrow row — no hand-set provenance).
	if err := st.UpsertHost(&store.Host{HostID: "peti-00-dead", CustomerID: "peti", APIKey: "oldkey"}); err != nil {
		t.Fatal(err)
	}
	if _, _, err := st.SaveHostEscrow("peti-00-dead", []byte("opaque"), "fp", "posture", "2026-07-01T00:00:00Z", ""); err != nil {
		t.Fatal(err)
	}
	if err := st.DeleteHost("peti-00-dead", true); err != nil {
		t.Fatalf("escrow-ack delete: %v", err)
	}

	rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	if rr.Code != 303 {
		t.Fatalf("enable with acked deletion record = %d (%s), want 303 (auto re-issue proceeds)", rr.Code, rr.Body.String())
	}
	if fake.provisionCalls != 1 || fake.reissueCalls != 1 {
		t.Errorf("calls = provision %d / reissue %d, want 1/1 (the EXISTING re-issue op, once)", fake.provisionCalls, fake.reissueCalls)
	}

	// Full effect: descriptor landed, generation bumped, fresh consume-once secret staged.
	desc, desiredJSON, gen := hostState(t, st)
	if desc == nil || !desc.Enabled || desc.Namespace != "peti" || desc.TokenID != "felhom@pbs!peti" {
		t.Fatalf("descriptor after auto re-issue = %+v (json %s)", desc, desiredJSON)
	}
	if gen != 1 {
		t.Errorf("generation = %d, want 1", gen)
	}
	if got, err := st.ConsumeHostPBSSecret("peti-01"); err != nil || got != "REISSUED-SECRET" {
		t.Fatalf("consume-once secret = (%q, %v), want the re-issued secret", got, err)
	}

	// The audit line: a stored hub-source event carrying the operator note.
	events, err := st.GetRecentEvents("peti", 10)
	if err != nil {
		t.Fatalf("events: %v", err)
	}
	var audit *store.Event
	for i := range events {
		if events[i].EventType == "pbsdr_auto_reissue" {
			audit = &events[i]
		}
	}
	if audit == nil {
		t.Fatal("no pbsdr_auto_reissue audit event stored")
	}
	if audit.Source != "hub" || !strings.Contains(audit.Message, "Previous key destroyed (acknowledged deletion)") {
		t.Errorf("audit event = %+v, want hub-source with the operator note", audit)
	}
	if !strings.Contains(audit.DetailsJSON, "peti-00-dead") {
		t.Errorf("audit details lack the deleted host: %s", audit.DetailsJSON)
	}
	if strings.Contains(logBuf.String(), "REISSUED-SECRET") {
		t.Error("secret leaked into the hub log")
	}
}

// F-14 scenario B (the never-silently-re-key law): a surviving tenancy WITHOUT an acked
// deletion record — none at all, or the latest one un-acked — keeps the current refusal and
// records ZERO re-issue calls (the exact non-effect).
// RED-PROOF (§10 B): removing the record check in pbsdrProvisionAtom (auto-reissue
// unconditionally) → the zero-reissue assertions here fail.
func TestPBSDR_F14NoRecordNeverRekeys(t *testing.T) {
	assertRefused := func(t *testing.T, fake *fakeTenancy, s *Server, st *store.Store) {
		t.Helper()
		rr := postUpdate(t, s, url.Values{"dr_tier": {"on"}})
		if rr.Code != 502 || !strings.Contains(rr.Body.String(), "Re-issue PBS credentials") {
			t.Fatalf("save = %d (%s), want the byte-unchanged 502 refusal", rr.Code, rr.Body.String())
		}
		if fake.reissueCalls != 0 {
			t.Errorf("reissue calls = %d, want 0 — SILENT RE-KEY", fake.reissueCalls)
		}
		if desc, _, gen := hostState(t, st); desc != nil || gen != 0 {
			t.Errorf("state written on refusal: desc=%+v gen=%d", desc, gen)
		}
		if _, err := st.ConsumeHostPBSSecret("peti-01"); err != sql.ErrNoRows {
			t.Errorf("a secret was staged on refusal (err %v)", err)
		}
	}

	t.Run("no deletion record at all", func(t *testing.T) {
		fake := &fakeTenancy{provisionErr: tenantsync.ErrTokenExists, secret: "S"}
		s, st, _ := newPBSDRServer(t, fake)
		assertRefused(t, fake, s, st)
	})

	t.Run("latest record un-acked", func(t *testing.T) {
		fake := &fakeTenancy{provisionErr: tenantsync.ErrTokenExists, secret: "S"}
		s, st, _ := newPBSDRServer(t, fake)
		// A deletion happened, but NOT through the escrow-ack flow (no escrow row → acked=false).
		if err := st.UpsertHost(&store.Host{HostID: "peti-00-dead", CustomerID: "peti", APIKey: "oldkey"}); err != nil {
			t.Fatal(err)
		}
		if err := st.DeleteHost("peti-00-dead", true); err != nil {
			t.Fatal(err)
		}
		assertRefused(t, fake, s, st)
	})
}

func TestPBSDR_Reissue(t *testing.T) {
	fake := &fakeTenancy{secret: "OLD-SECRET"}
	s, st, logBuf := newPBSDRServer(t, fake)
	postUpdate(t, s, url.Values{"dr_tier": {"on"}})
	st.ConsumeHostPBSSecret("peti-01") // agent already consumed; the dead-end scenario

	fake.secret = "FRESH-SECRET"
	req := httptest.NewRequest("POST", "/configs/peti/pbsdr-reissue", nil)
	rr := httptest.NewRecorder()
	s.handlePBSDRReissue(rr, req, "peti")
	if rr.Code != 303 {
		t.Fatalf("reissue = %d (%s), want 303", rr.Code, rr.Body.String())
	}
	if fake.reissueCalls != 1 {
		t.Errorf("reissue calls = %d, want 1", fake.reissueCalls)
	}
	got, err := st.ConsumeHostPBSSecret("peti-01")
	if err != nil || got != "FRESH-SECRET" {
		t.Fatalf("fresh secret consume = (%q, %v)", got, err)
	}
	if _, err := st.ConsumeHostPBSSecret("peti-01"); err != sql.ErrNoRows {
		t.Error("the old secret path survived the re-issue")
	}
	if _, _, gen := hostState(t, st); gen != 2 {
		t.Errorf("generation = %d, want 2 (provision + reissue)", gen)
	}
	if strings.Contains(logBuf.String(), "FRESH-SECRET") || strings.Contains(logBuf.String(), "OLD-SECRET") {
		t.Error("secret leaked into the hub log")
	}
}

func TestPBSDR_ReissueRequiresProvisionedState(t *testing.T) {
	fake := &fakeTenancy{secret: "S"}
	s, _, _ := newPBSDRServer(t, fake)
	req := httptest.NewRequest("POST", "/configs/peti/pbsdr-reissue", nil)
	rr := httptest.NewRecorder()
	s.handlePBSDRReissue(rr, req, "peti")
	if rr.Code != 400 {
		t.Fatalf("reissue without a provisioned tier = %d, want 400", rr.Code)
	}
	if fake.reissueCalls != 0 {
		t.Errorf("endpoint touched without a descriptor (%d calls)", fake.reissueCalls)
	}
}

// The form render leg: the section reflects the provisioned descriptor (checkbox + namespace line)
// and the fresh-form default storage id.
func TestPBSDR_FormRendersState(t *testing.T) {
	fake := &fakeTenancy{secret: "S"}
	s, _, _ := newPBSDRServer(t, fake)
	postUpdate(t, s, url.Values{"dr_tier": {"on"}})

	// v0.48.0 edit-a: the standalone GET edit page is a redirect now — the form renders embedded
	// in the customer page's Edit tab (the same config_form_body sub-template), so assert there.
	req := httptest.NewRequest("GET", "/customers/peti", nil)
	rr := httptest.NewRecorder()
	s.handleCustomerUnified(rr, req, "peti")
	out := rr.Body.String()
	if !strings.Contains(out, `name="dr_tier" checked`) {
		t.Error("DR-tier checkbox not checked after provisioning")
	}
	if !strings.Contains(out, "descriptor provisioned (namespace peti, token felhom@pbs!peti)") {
		t.Error("provisioned cascade stage line missing")
	}
	// The cascade (v0.51.0 scenario D): all four stages render as done on a fully-applied box.
	if strings.Count(out, `class="badge badge-neutral">waiting</span>`) > 1 {
		// escrow may still be waiting in this harness; every other stage must be done
		t.Errorf("more than the escrow stage still waiting:\n%s", out[strings.Index(out, "DR tier (PBS, ep0)"):][:1200])
	}
	if !strings.Contains(out, "pbsdr-reissue") {
		t.Error("re-issue button missing")
	}
	if strings.Contains(out, "S\"") && strings.Contains(out, "token_secret") {
		t.Error("secret-ish content rendered")
	}

	// Deadline check on the detached-ctx behavior is out of scope here; the view path must
	// stay read-only — a render must not have touched provisioning.
	if fake.provisionCalls != 1 {
		t.Errorf("rendering the form called provision (%d calls)", fake.provisionCalls)
	}
	_ = time.Second
}
