package auth

import (
	"encoding/base64"
	"strings"
	"testing"
)

func TestNewSessionControlProofUsesCanonicalThirtyTwoByteSecretAndHashOnlyVerifier(t *testing.T) {
	seen := make(map[string]struct{})
	for i := 0; i < 64; i++ {
		proof, proofHash, err := newSessionControlProof()
		if err != nil {
			t.Fatalf("newSessionControlProof: %v", err)
		}
		decoded, err := base64.RawURLEncoding.DecodeString(proof)
		if err != nil || len(decoded) != sessionControlProofEntropyBytes || base64.RawURLEncoding.EncodeToString(decoded) != proof {
			t.Fatalf("proof %q is not canonical 32-byte raw base64url: len=%d err=%v", proof, len(decoded), err)
		}
		if len(proofHash) != 64 || proofHash != strings.ToLower(proofHash) || proofHash == proof {
			t.Fatalf("proof hash = %q, want lowercase SHA-256 hex distinct from raw proof", proofHash)
		}
		if _, duplicate := seen[proof]; duplicate {
			t.Fatalf("duplicate control proof generated: %q", proof)
		}
		seen[proof] = struct{}{}
		if got, ok := matchingSessionControlProofHash(proof, &proofHash); !ok || got != proofHash {
			t.Fatalf("matchingSessionControlProofHash = %q, %v; want %q, true", got, ok, proofHash)
		}
	}
}

func TestSessionControlProofMatchingRejectsWrongMalformedAndCorruptValues(t *testing.T) {
	proof, proofHash, err := newSessionControlProof()
	if err != nil {
		t.Fatal(err)
	}
	otherProof, _, err := newSessionControlProof()
	if err != nil {
		t.Fatal(err)
	}
	corruptHash := "not-a-sha256-verifier"
	for _, test := range []struct {
		name     string
		proof    string
		expected *string
	}{
		{name: "empty", proof: "", expected: &proofHash},
		{name: "wrong proof", proof: otherProof, expected: &proofHash},
		{name: "noncanonical padding", proof: proof + "=", expected: &proofHash},
		{name: "short decoded secret", proof: base64.RawURLEncoding.EncodeToString([]byte("short")), expected: &proofHash},
		{name: "missing verifier", proof: proof, expected: nil},
		{name: "corrupt verifier", proof: proof, expected: &corruptHash},
	} {
		t.Run(test.name, func(t *testing.T) {
			if got, ok := matchingSessionControlProofHash(test.proof, test.expected); ok || got != "" {
				t.Fatalf("matchingSessionControlProofHash = %q, %v; want empty, false", got, ok)
			}
		})
	}
}
