package middleware

import (
	"context"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/google/uuid"
	"github.com/stretchr/testify/require"
)

// fakeAuth is a stub SessionResolver used by the auth middleware tests.
// It returns either the configured user (ok=true) or the configured error,
// so each test can target a specific branch in Auth without spinning up
// the real user.Service (which lives in a later phase).
type fakeAuth struct {
	user uuid.UUID
	err  error
}

func (f fakeAuth) ResolveSession(_ context.Context, _ string) (uuid.UUID, bool, error) {
	if f.err != nil {
		return uuid.Nil, false, f.err
	}
	return f.user, true, nil
}

func TestAuth_NoTokenAllowedWhenOptional(t *testing.T) {
	called := false
	mw := Auth(fakeAuth{}, AuthOptions{Optional: true})
	h := mw(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
		called = true
		_, ok := UserIDFromContext(r.Context())
		require.False(t, ok)
	}))
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
	require.True(t, called)
	require.Equal(t, http.StatusOK, rec.Code)
}

func TestAuth_NoTokenRejectedWhenRequired(t *testing.T) {
	mw := Auth(fakeAuth{}, AuthOptions{})
	h := mw(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		t.Fatal("handler should not be called")
	}))
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
	require.Equal(t, http.StatusUnauthorized, rec.Code)
}

func TestAuth_ValidTokenPopulatesContext(t *testing.T) {
	uid := uuid.New()
	mw := Auth(fakeAuth{user: uid}, AuthOptions{})
	h := mw(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
		got, ok := UserIDFromContext(r.Context())
		require.True(t, ok)
		require.Equal(t, uid, got)
	}))
	req := httptest.NewRequest("GET", "/", nil)
	req.Header.Set("Authorization", "Bearer tok")
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, req)
	require.Equal(t, http.StatusOK, rec.Code)
}

func TestAuth_BadTokenReturns401(t *testing.T) {
	mw := Auth(fakeAuth{err: errors.New("invalid")}, AuthOptions{})
	h := mw(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
		t.Fatal("handler should not be called")
	}))
	req := httptest.NewRequest("GET", "/", nil)
	req.Header.Set("Authorization", "Bearer x")
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, req)
	require.Equal(t, http.StatusUnauthorized, rec.Code)
}

func TestAuth_WebSocketTokenFromSubprotocol(t *testing.T) {
	uid := uuid.New()
	mw := Auth(fakeAuth{user: uid}, AuthOptions{})
	h := mw(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
		got, ok := UserIDFromContext(r.Context())
		require.True(t, ok)
		require.Equal(t, uid, got)
	}))
	req := httptest.NewRequest("GET", "/", nil)
	req.Header.Set("Sec-WebSocket-Protocol", "acp-v1, acw-token.ws-tok")
	rec := httptest.NewRecorder()
	h.ServeHTTP(rec, req)
	require.Equal(t, http.StatusOK, rec.Code)
}
