//go:build test
// +build test

package handlers

// Package handlers contains tests for core middleware and health checks.
//
// Test Coverage:
//   - Health check: Basic HTTP 200 response
//   - RequireAuth middleware: Blocks unauthenticated requests (401), allows valid JWT (200)
//   - RequireRole middleware: Blocks non-admin users (403), allows admins (200)
//   - Integration test: Full user flow with JWT auth and context propagation
//
// Note: These tests focus on middleware behavior, not specific handler business logic.
import (
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"crussell/mw"
	"crussell/testutils"
	"crussell/testutils/fixtures"
	"crussell/testutils/jwt"
)

// TestHealthCheck verifies the health check endpoint returns HTTP 200 OK.
// This test ensures the basic HTTP server is responding and the health
// check handler is properly wired up to return a status response.
func TestHealthCheck(t *testing.T) {
	t.Parallel()

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"ok"}`))
	})

	server := httptest.NewServer(handler)
	defer server.Close()

	resp, err := server.Client().Get(server.URL)
	if err != nil {
		t.Fatalf("failed to make request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("expected status 200, got %d", resp.StatusCode)
	}
}

// TestRequireAuthMiddleware verifies the JWT authentication middleware correctly
// blocks unauthenticated requests and allows valid JWT tokens through.
// It tests three scenarios: missing auth header (401), valid token (200 with
// user context), and invalid token (401).
func TestRequireAuthMiddleware(t *testing.T) {
	t.Parallel()

	handler := mw.RequireAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		userID, _ := r.Context().Value(mw.UserIDKey).(string)
		role, _ := r.Context().Value(mw.UserRoleKey).(string)
		w.Write([]byte(`{"user_id":"` + userID + `","role":"` + role + `"}`))
	}))

	t.Run("no auth header returns 401", func(t *testing.T) {
		req := httptest.NewRequest("GET", "/", nil)
		w := httptest.NewRecorder()
		handler.ServeHTTP(w, req)

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

	t.Run("valid token passes auth", func(t *testing.T) {
		jwt.Init()
		token := jwt.GenerateTestToken("test-user-123", "verified_email")

		req := httptest.NewRequest("GET", "/", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		w := httptest.NewRecorder()
		handler.ServeHTTP(w, req)

		if w.Code != http.StatusOK {
			t.Errorf("expected status 200, got %d", w.Code)
		}

		body, _ := io.ReadAll(w.Body)
		var resp map[string]string
		json.Unmarshal(body, &resp)

		if resp["user_id"] != "test-user-123" {
			t.Errorf("expected user_id test-user-123, got %s", resp["user_id"])
		}
		if resp["role"] != "verified_email" {
			t.Errorf("expected role verified_email, got %s", resp["role"])
		}
	})

	t.Run("invalid token returns 401", func(t *testing.T) {
		jwt.Init()
		req := httptest.NewRequest("GET", "/", nil)
		req.Header.Set("Authorization", "Bearer invalid-token")
		w := httptest.NewRecorder()
		handler.ServeHTTP(w, req)

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

// TestRequireRoleMiddleware verifies the role-based access control middleware
// correctly blocks non-admin users (403) and allows admin users (200) to access
// protected resources. It chains RequireAuth before RequireRole to populate
// the role in the request context.
func TestRequireRoleMiddleware(t *testing.T) {
	t.Parallel()

	// Chain RequireAuth before RequireRole to set the role in context
	// RequireRole expects role to be in context, but that's only set by RequireAuth
	adminOnlyHandler := mw.RequireAuth(mw.RequireRole("admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"success":true}`))
	})))

	t.Run("admin role passes", func(t *testing.T) {
		jwt.Init()
		token := jwt.GenerateAdminToken()

		req := httptest.NewRequest("GET", "/", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		w := httptest.NewRecorder()
		adminOnlyHandler.ServeHTTP(w, req)

		if w.Code != http.StatusOK {
			t.Errorf("expected status 200, got %d", w.Code)
		}
	})

	t.Run("non-admin role returns 403", func(t *testing.T) {
		jwt.Init()
		token := jwt.GenerateUserToken("test-user")

		req := httptest.NewRequest("GET", "/", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		w := httptest.NewRecorder()
		adminOnlyHandler.ServeHTTP(w, req)

		if w.Code != http.StatusForbidden {
			t.Errorf("expected status 403, got %d", w.Code)
		}
	})
}

// TestIntegration_UserFlow tests the full authentication flow end-to-end,
// verifying that JWT tokens are properly validated, user ID is extracted from
// the token, and context values are correctly propagated to handlers.
// This is an integration test that validates the complete middleware chain.
func TestIntegration_UserFlow(t *testing.T) {
	t.Parallel()

	if testing.Short() {
		t.Skip("skipping integration test in short mode")
	}

	_, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUser(tx)
	if err != nil {
		t.Fatalf("failed to create test user: %v", err)
	}

	token := jwt.GenerateUserToken(userID)

	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		authHeader := r.Header.Get("Authorization")
		if authHeader == "" {
			http.Error(w, "no auth", http.StatusUnauthorized)
			return
		}

		userIDCtx, ok := r.Context().Value(mw.UserIDKey).(string)
		if !ok {
			http.Error(w, "no user id in context", http.StatusInternalServerError)
			return
		}

		w.Write([]byte(`{"user_id":"` + userIDCtx + `"}`))
	})

	req := httptest.NewRequest("GET", "/", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	w := httptest.NewRecorder()

	mw.RequireAuth(handler).ServeHTTP(w, req)

	if w.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", w.Code)
	}
}
