package rest

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

	"github.com/gin-gonic/gin"
)

func mkAdminPaginationCtx(rawQuery string) (*gin.Context, *httptest.ResponseRecorder) {
	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest(http.MethodGet, "/?"+rawQuery, nil)
	return c, w
}

func TestParsePositiveLimit(t *testing.T) {
	t.Run("default when absent", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("")
		got, ok := parsePositiveLimit(c, "limit", 50, 500)
		if !ok || got != 50 {
			t.Fatalf("got %d ok=%v, want 50 true", got, ok)
		}
	})
	t.Run("valid within range", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("limit=10")
		got, ok := parsePositiveLimit(c, "limit", 50, 500)
		if !ok || got != 10 {
			t.Fatalf("got %d ok=%v, want 10 true", got, ok)
		}
	})
	t.Run("clamps to max", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("limit=9999")
		got, ok := parsePositiveLimit(c, "limit", 50, 500)
		if !ok || got != 500 {
			t.Fatalf("got %d ok=%v, want 500 (clamped) true", got, ok)
		}
	})
	t.Run("rejects non-positive", func(t *testing.T) {
		c, w := mkAdminPaginationCtx("limit=0")
		_, ok := parsePositiveLimit(c, "limit", 50, 500)
		if ok || w.Code != http.StatusBadRequest {
			t.Fatalf("ok=%v code=%d, want false 400", ok, w.Code)
		}
	})
	t.Run("rejects non-numeric", func(t *testing.T) {
		c, w := mkAdminPaginationCtx("limit=abc")
		_, ok := parsePositiveLimit(c, "limit", 50, 500)
		if ok || w.Code != http.StatusBadRequest {
			t.Fatalf("ok=%v code=%d, want false 400", ok, w.Code)
		}
	})
}

func TestParseOptionalNonNegativeInt(t *testing.T) {
	t.Run("default when absent", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("")
		got, ok := parseOptionalNonNegativeInt(c, "offset", 0)
		if !ok || got != 0 {
			t.Fatalf("got %d ok=%v, want 0 true", got, ok)
		}
	})
	t.Run("zero is allowed", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("offset=0")
		got, ok := parseOptionalNonNegativeInt(c, "offset", 5)
		if !ok || got != 0 {
			t.Fatalf("got %d ok=%v, want 0 true", got, ok)
		}
	})
	t.Run("rejects negative", func(t *testing.T) {
		c, w := mkAdminPaginationCtx("offset=-1")
		_, ok := parseOptionalNonNegativeInt(c, "offset", 0)
		if ok || w.Code != http.StatusBadRequest {
			t.Fatalf("ok=%v code=%d, want false 400", ok, w.Code)
		}
	})
}

func TestParseOptionalRFC3339Nano(t *testing.T) {
	t.Run("zero when absent", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("")
		got, ok := parseOptionalRFC3339Nano(c, "since")
		if !ok || !got.IsZero() {
			t.Fatalf("got %v ok=%v, want zero true", got, ok)
		}
	})
	t.Run("valid timestamp", func(t *testing.T) {
		c, _ := mkAdminPaginationCtx("since=2026-06-09T12:00:00Z")
		got, ok := parseOptionalRFC3339Nano(c, "since")
		want := time.Date(2026, 6, 9, 12, 0, 0, 0, time.UTC)
		if !ok || !got.Equal(want) {
			t.Fatalf("got %v ok=%v, want %v true", got, ok, want)
		}
	})
	t.Run("rejects unparseable", func(t *testing.T) {
		c, w := mkAdminPaginationCtx("since=not-a-time")
		_, ok := parseOptionalRFC3339Nano(c, "since")
		if ok || w.Code != http.StatusBadRequest {
			t.Fatalf("ok=%v code=%d, want false 400", ok, w.Code)
		}
	})
}
