package api

// PBS DR SLICE 1 — the agent-facing consume-once endpoint. The contract: 200 with the secret
// EXACTLY once per stored value, 404 after (and when nothing is stored), 403 on a foreign
// host's key WITHOUT burning the secret, 401 unauthenticated.

import (
	"encoding/json"
	"net/http"
	"testing"

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

func TestConsumePBSToken_OnceThen404(t *testing.T) {
	h, st, _ := newTestHandler(t)
	st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY"})
	if _, err := st.SaveHostPBSSecret("h1", "tok-secret-1"); err != nil {
		t.Fatalf("seed secret: %v", err)
	}

	rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "HKEY", "")
	if rr.Code != http.StatusOK {
		t.Fatalf("first consume = %d, want 200 (%s)", rr.Code, rr.Body.String())
	}
	var resp map[string]string
	if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
		t.Fatalf("response parse: %v", err)
	}
	if resp["token_secret"] != "tok-secret-1" {
		t.Errorf("token_secret = %q, want tok-secret-1", resp["token_secret"])
	}

	// Consume-once: the second fetch MUST 404. (Red-proof: drop the consumed_at UPDATE in
	// store.ConsumeHostPBSSecret → this returns 200 with the secret again → FAIL.)
	rr = do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "HKEY", "")
	if rr.Code != http.StatusNotFound {
		t.Fatalf("second consume = %d (%s), want 404 — single-use broken", rr.Code, rr.Body.String())
	}
}

func TestConsumePBSToken_ForeignKeyForbiddenAndSecretSurvives(t *testing.T) {
	h, st, _ := newTestHandler(t)
	st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY"})
	st.UpsertHost(&store.Host{HostID: "h2", CustomerID: "c2", APIKey: "HKEY2"})
	if _, err := st.SaveHostPBSSecret("h1", "tok-secret-1"); err != nil {
		t.Fatalf("seed secret: %v", err)
	}

	// h2's key against h1's path → 403, and the attempt must NOT consume h1's secret.
	rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "HKEY2", "")
	if rr.Code != http.StatusForbidden {
		t.Fatalf("foreign-key consume = %d, want 403", rr.Code)
	}
	rr = do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "HKEY", "")
	if rr.Code != http.StatusOK {
		t.Fatalf("own consume after foreign 403 = %d, want 200 — the 403 burned the secret", rr.Code)
	}
}

func TestConsumePBSToken_AuthMatrix(t *testing.T) {
	h, st, _ := newTestHandler(t)
	st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY"})
	_, _ = st.SaveHostPBSSecret("h1", "tok-secret-1")
	if rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "", ""); rr.Code != http.StatusUnauthorized {
		t.Errorf("unauthenticated = %d, want 401", rr.Code)
	}
	if rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "bogus", ""); rr.Code != http.StatusUnauthorized {
		t.Errorf("bogus key = %d, want 401", rr.Code)
	}
	// The global key may consume on a host's behalf (operator recovery path).
	if rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", globalKey, ""); rr.Code != http.StatusOK {
		t.Errorf("global key = %d, want 200", rr.Code)
	}
	// Nothing stored (just consumed above) → 404, not an error leak.
	if rr := do(h, http.MethodPost, "/hosts/h1/pbs/consume-token", "HKEY", ""); rr.Code != http.StatusNotFound {
		t.Errorf("post-consume = %d, want 404", rr.Code)
	}
}
