package auth

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

// OptionalMiddleware guards the read path of GET /uploads/{key}, where public
// images must stay reachable anonymously but private payment proofs need a
// caller identity. It must never turn a presented-but-invalid token into an
// anonymous request.
func TestOptionalMiddleware(t *testing.T) {
	u := fakeHashUser(1, "buyer@b2b.com", RoleBuyer, true)
	f := newFake(u)
	svc := NewService(f, time.UTC, 0)
	res, err := svc.Login(t.Context(), "buyer@b2b.com", "secret123")
	if err != nil {
		t.Fatal(err)
	}

	var seen *User
	h := OptionalMiddleware(svc)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		seen = UserFrom(r.Context())
		w.WriteHeader(http.StatusOK)
	}))

	cases := []struct {
		name       string
		authz      string
		wantCode   int
		wantUserID int64 // 0 = anonymous
	}{
		{name: "no header stays anonymous", wantCode: http.StatusOK},
		{name: "malformed header stays anonymous", authz: "Token abc", wantCode: http.StatusOK},
		{name: "valid token authenticates", authz: "Bearer " + res.Token, wantCode: http.StatusOK, wantUserID: 1},
		{name: "unknown token is rejected", authz: "Bearer nonsense", wantCode: http.StatusUnauthorized},
		{name: "expired token is rejected", authz: "Bearer " + expiredToken(f), wantCode: http.StatusUnauthorized},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			seen = nil
			req := httptest.NewRequest(http.MethodGet, "/uploads/x.png", nil)
			if tc.authz != "" {
				req.Header.Set("Authorization", tc.authz)
			}
			rr := httptest.NewRecorder()
			h.ServeHTTP(rr, req)
			if rr.Code != tc.wantCode {
				t.Fatalf("status = %d, want %d", rr.Code, tc.wantCode)
			}
			got := int64(0)
			if seen != nil {
				got = seen.ID
			}
			if got != tc.wantUserID {
				t.Fatalf("user on context = %d, want %d", got, tc.wantUserID)
			}
		})
	}
}

// expiredToken registers a token that authenticates but is past its expiry.
func expiredToken(f *fakeStore) string {
	past := time.Now().Add(-time.Hour)
	tok := "expired-token"
	f.tokens[HashToken(tok)] = &Token{ID: 99, TokenHash: HashToken(tok), TokenableID: 1, ExpiresAt: &past}
	return tok
}
