package auth

import (
	"crypto/sha256"
	"encoding/base64"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"golang.org/x/crypto/bcrypt"
)

func TestMintSessionToken_ReturnsUrlSafeBase64(t *testing.T) {
	token, err := MintSessionToken()
	if err != nil {
		t.Fatalf("MintSessionToken: %v", err)
	}
	if len(token) < 40 {
		t.Errorf("token length = %d, want >= 40 (32B base64 url-safe)", len(token))
	}
	if strings.ContainsAny(token, "+/=") {
		t.Errorf("token %q contains non-url-safe chars", token)
	}
}

func TestHashSessionToken_IsDeterministicSHA256(t *testing.T) {
	token := "test-token-xyz"
	want := sha256.Sum256([]byte(token))

	got := HashSessionToken(token)
	if len(got) != sha256.Size {
		t.Fatalf("hash length = %d, want %d", len(got), sha256.Size)
	}
	if base64.StdEncoding.EncodeToString(got) != base64.StdEncoding.EncodeToString(want[:]) {
		t.Errorf("hash = %x, want %x", got, want)
	}
}

func TestVerifyPassword(t *testing.T) {
	hash, err := bcrypt.GenerateFromPassword([]byte("hunter2"), bcrypt.MinCost)
	if err != nil {
		t.Fatalf("GenerateFromPassword: %v", err)
	}
	if !VerifyPassword(string(hash), "hunter2") {
		t.Error("correct password rejected")
	}
	if VerifyPassword(string(hash), "wrong") {
		t.Error("wrong password accepted")
	}
	if VerifyPassword("not-a-hash", "hunter2") {
		t.Error("malformed hash accepted")
	}
}

func TestRequireUser_RejectsWhenNoCookieOrBearer(t *testing.T) {
	next := http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
		t.Fatal("handler must not be called")
	})
	h := RequireUser(nil, nil)(next)

	req := httptest.NewRequest(http.MethodGet, "/api/me", nil)
	w := httptest.NewRecorder()
	h.ServeHTTP(w, req)

	if w.Code != http.StatusUnauthorized {
		t.Errorf("status = %d, want 401", w.Code)
	}
}

func TestExtractBearerToken(t *testing.T) {
	cases := []struct {
		header string
		want   string
	}{
		{"", ""},
		{"Bearer abc", "abc"},
		{"bearer abc", "abc"},
		{"Token abc", ""},
		{"Bearer", ""},
		{"Bearer   whitespace-token  ", "whitespace-token"},
	}
	for _, c := range cases {
		got := extractBearerToken(c.header)
		if got != c.want {
			t.Errorf("extractBearerToken(%q) = %q, want %q", c.header, got, c.want)
		}
	}
}
