package apikey

import (
	"bytes"
	"context"
	"testing"
)

func TestBootstrapCompatibilityUsageCounter(t *testing.T) {
	authenticator := NewAuthenticator(nil, nil, "temporary-bootstrap-key")
	if err := authenticator.Authenticate(context.Background(), "temporary-bootstrap-key"); err != nil {
		t.Fatal(err)
	}
	if authenticator.BootstrapUses() != 1 {
		t.Fatalf("unexpected bootstrap usage count %d", authenticator.BootstrapUses())
	}
}

func TestGenerateAndDigest(t *testing.T) {
	first, prefix, hash, err := Generate()
	if err != nil {
		t.Fatal(err)
	}
	if len(first) < 40 || len(prefix) != 16 || prefix != first[:16] {
		t.Fatal("invalid API key format")
	}
	computed, _ := Digest(first)
	if !bytes.Equal(hash, computed) {
		t.Fatal("stored digest differs")
	}
	second, _, _, err := Generate()
	if err != nil || second == first {
		t.Fatal("API keys must be independently random")
	}
}

func TestScopes(t *testing.T) {
	if !HasScope([]string{"gateway:invoke"}, "gateway:invoke") || !HasScope([]string{"*"}, "gateway:invoke") {
		t.Fatal("expected scope is missing")
	}
	if HasScope([]string{"gateway:read"}, "gateway:invoke") {
		t.Fatal("unexpected scope accepted")
	}
}

// TestInvokeScope pins the regression where portal application runtime
// credentials ("application:run") could never reach the gateway: the scope
// check only admitted "gateway:invoke", so every hosted application chat
// returned 401 even though the credential is legitimate.
func TestInvokeScope(t *testing.T) {
	accept := map[string][]string{
		"gateway key":             {"gateway:invoke"},
		"admin wildcard":          {"*"},
		"application runtime key": {"application:run"},
		"runtime + read":          {"application:run", "gateway:read"},
	}
	for name, scopes := range accept {
		if !invokeScope(scopes) {
			t.Fatalf("invokeScope(%v) = false, want true for %s", scopes, name)
		}
	}
	reject := map[string][]string{
		"read-only":             {"gateway:read"},
		"unrelated scope":       {"workbench:run"},
		"empty":                 {},
		"runtime scope missing": {"gateway:read", "workbench:run"},
	}
	for name, scopes := range reject {
		if invokeScope(scopes) {
			t.Fatalf("invokeScope(%v) = true, want false for %s", scopes, name)
		}
	}
}
