package reqmodifier

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestRewriteBracketParams(t *testing.T) {
	// Set Gin to test mode to avoid unnecessary logging
	gin.SetMode(gin.TestMode)

	tests := []struct {
		name           string
		queryParams    map[string][]string
		expectedParams map[string][]string
		expectedRaw    string
		shouldChange   bool
	}{
		{
			name: "single bracket parameter",
			queryParams: map[string][]string{
				"arr[]": {"1", "2", "3"},
			},
			expectedParams: map[string][]string{
				"arr": {"1", "2", "3"},
			},
			expectedRaw:  "arr=1&arr=2&arr=3",
			shouldChange: true,
		},
		{
			name: "multiple bracket parameters",
			queryParams: map[string][]string{
				"users[]": {"alice", "bob"},
				"roles[]": {"admin", "user"},
			},
			expectedParams: map[string][]string{
				"users": {"alice", "bob"},
				"roles": {"admin", "user"},
			},
			expectedRaw:  "roles=admin&roles=user&users=alice&users=bob",
			shouldChange: true,
		},
		{
			name: "mixed bracket and regular parameters",
			queryParams: map[string][]string{
				"items[]": {"apple", "banana"},
				"count":   {"5"},
				"active":  {"true"},
			},
			expectedParams: map[string][]string{
				"items":  {"apple", "banana"},
				"count":  {"5"},
				"active": {"true"},
			},
			expectedRaw:  "active=true&count=5&items=apple&items=banana",
			shouldChange: true,
		},
		{
			name: "no bracket parameters",
			queryParams: map[string][]string{
				"name":  {"john"},
				"email": {"john@example.com"},
			},
			expectedParams: map[string][]string{
				"name":  {"john"},
				"email": {"john@example.com"},
			},
			expectedRaw:  "email=john%40example.com&name=john",
			shouldChange: false,
		},
		{
			name: "single value bracket parameter",
			queryParams: map[string][]string{
				"id[]": {"123"},
			},
			expectedParams: map[string][]string{
				"id": {"123"},
			},
			expectedRaw:  "id=123",
			shouldChange: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Create a new Gin context for each test
			w := httptest.NewRecorder()
			c, _ := gin.CreateTestContext(w)

			// Create a new HTTP request
			req, err := http.NewRequest(http.MethodGet, "/test", http.NoBody)
			require.NoError(t, err)

			// Set the query parameters
			req.URL.RawQuery = buildQueryString(tt.queryParams)
			c.Request = req

			// Call the middleware
			RewriteBracketParams()(c)

			// Verify the query parameters were rewritten correctly
			query := c.Request.URL.Query()

			// Check that all expected parameters exist with correct values
			for expectedKey, expectedValues := range tt.expectedParams {
				actualValues, exists := query[expectedKey]
				assert.True(t, exists, "Expected key '%s' to exist", expectedKey)
				if exists {
					assert.ElementsMatch(t, expectedValues, actualValues,
						"Values for key '%s' don't match", expectedKey)
				}
			}

			// Check that no bracket parameters remain
			for key := range query {
				assert.False(t, strings.HasSuffix(key, "[]"),
					"Bracket parameter '%s' should have been removed", key)
			}

			// Verify the RawQuery was updated correctly
			if tt.shouldChange {
				assert.Equal(t, tt.expectedRaw, c.Request.URL.RawQuery,
					"RawQuery should match expected value")
			}
		})
	}
}

// Helper function to build query string from map
func buildQueryString(params map[string][]string) string {
	values := url.Values{}
	for key, vals := range params {
		for _, val := range vals {
			values.Add(key, val)
		}
	}
	return values.Encode()
}
