package helper

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestRespondWithError(t *testing.T) {
	testCases := []struct {
		name       string
		statusCode int
		message    string
	}{
		{"Bad Request", http.StatusBadRequest, "Invalid input"},
		{"Unauthorized", http.StatusUnauthorized, "Not authenticated"},
		{"Forbidden", http.StatusForbidden, "Access denied"},
		{"Not Found", http.StatusNotFound, "Resource not found"},
		{"Internal Error", http.StatusInternalServerError, "Server error"},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			recorder := httptest.NewRecorder()
			RespondWithError(recorder, tc.statusCode, tc.message)

			// Check status code
			if recorder.Code != tc.statusCode {
				t.Errorf("Expected status code %d, got %d", tc.statusCode, recorder.Code)
			}

			// Check content type
			contentType := recorder.Header().Get("Content-Type")
			if contentType != "application/json" {
				t.Errorf("Expected Content-Type 'application/json', got '%s'", contentType)
			}

			// Parse response body
			var response map[string]string
			err := json.Unmarshal(recorder.Body.Bytes(), &response)
			if err != nil {
				t.Fatalf("Failed to parse response: %v", err)
			}

			// Check error message
			if response["error"] != tc.message {
				t.Errorf("Expected error message '%s', got '%s'", tc.message, response["error"])
			}
		})
	}
}

func TestRespondWithMessage(t *testing.T) {
	testCases := []string{
		"Operation successful",
		"User created",
		"Email sent",
		"Task completed",
	}

	for _, message := range testCases {
		t.Run(message, func(t *testing.T) {
			recorder := httptest.NewRecorder()
			RespondWithMessage(recorder, message)

			// Check status code (should default to 200)
			if recorder.Code != http.StatusOK {
				t.Errorf("Expected status code %d, got %d", http.StatusOK, recorder.Code)
			}

			// Parse response body
			var response map[string]string
			err := json.Unmarshal(recorder.Body.Bytes(), &response)
			if err != nil {
				t.Fatalf("Failed to parse response: %v", err)
			}

			// Check message
			if response["message"] != message {
				t.Errorf("Expected message '%s', got '%s'", message, response["message"])
			}
		})
	}
}

func TestRespondWithJSON(t *testing.T) {
	testCases := []struct {
		name       string
		statusCode int
		data       interface{}
	}{
		{
			name:       "Simple object",
			statusCode: http.StatusOK,
			data:       map[string]string{"key": "value"},
		},
		{
			name:       "Array",
			statusCode: http.StatusOK,
			data:       []string{"item1", "item2", "item3"},
		},
		{
			name:       "Nested object",
			statusCode: http.StatusCreated,
			data: map[string]interface{}{
				"user": map[string]string{
					"name":  "John",
					"email": "john@example.com",
				},
				"status": "active",
			},
		},
		{
			name:       "Number",
			statusCode: http.StatusOK,
			data:       map[string]int{"count": 42},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			recorder := httptest.NewRecorder()
			RespondWithJSON(recorder, tc.statusCode, tc.data)

			// Check status code
			if recorder.Code != tc.statusCode {
				t.Errorf("Expected status code %d, got %d", tc.statusCode, recorder.Code)
			}

			// Check content type
			contentType := recorder.Header().Get("Content-Type")
			if contentType != "application/json" {
				t.Errorf("Expected Content-Type 'application/json', got '%s'", contentType)
			}

			// Verify response can be parsed as JSON
			var response interface{}
			err := json.Unmarshal(recorder.Body.Bytes(), &response)
			if err != nil {
				t.Fatalf("Failed to parse response as JSON: %v", err)
			}
		})
	}
}

func TestRespondWithErrorEmptyMessage(t *testing.T) {
	recorder := httptest.NewRecorder()
	RespondWithError(recorder, http.StatusBadRequest, "")

	var response map[string]string
	err := json.Unmarshal(recorder.Body.Bytes(), &response)
	if err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if _, exists := response["error"]; !exists {
		t.Error("Response should contain 'error' key even with empty message")
	}
}

func TestRespondWithJSONNilData(t *testing.T) {
	recorder := httptest.NewRecorder()
	RespondWithJSON(recorder, http.StatusOK, nil)

	if recorder.Code != http.StatusOK {
		t.Errorf("Expected status code %d, got %d", http.StatusOK, recorder.Code)
	}

	body := recorder.Body.String()
	if body != "null\n" {
		t.Errorf("Expected 'null', got '%s'", body)
	}
}

func TestRespondWithErrorStatusCodes(t *testing.T) {
	statusCodes := []int{
		http.StatusBadRequest,
		http.StatusUnauthorized,
		http.StatusForbidden,
		http.StatusNotFound,
		http.StatusMethodNotAllowed,
		http.StatusConflict,
		http.StatusUnprocessableEntity,
		http.StatusTooManyRequests,
		http.StatusInternalServerError,
		http.StatusServiceUnavailable,
	}

	for _, code := range statusCodes {
		t.Run(http.StatusText(code), func(t *testing.T) {
			recorder := httptest.NewRecorder()
			RespondWithError(recorder, code, "Test error")

			if recorder.Code != code {
				t.Errorf("Expected status code %d, got %d", code, recorder.Code)
			}
		})
	}
}

func TestRespondWithJSONComplex(t *testing.T) {
	type User struct {
		ID       int      `json:"id"`
		Name     string   `json:"name"`
		Email    string   `json:"email"`
		Roles    []string `json:"roles"`
		IsActive bool     `json:"is_active"`
	}

	user := User{
		ID:       123,
		Name:     "Test User",
		Email:    "test@example.com",
		Roles:    []string{"admin", "user"},
		IsActive: true,
	}

	recorder := httptest.NewRecorder()
	RespondWithJSON(recorder, http.StatusOK, user)

	var decoded User
	err := json.Unmarshal(recorder.Body.Bytes(), &decoded)
	if err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if decoded.ID != user.ID {
		t.Errorf("Expected ID %d, got %d", user.ID, decoded.ID)
	}

	if decoded.Name != user.Name {
		t.Errorf("Expected Name '%s', got '%s'", user.Name, decoded.Name)
	}

	if decoded.Email != user.Email {
		t.Errorf("Expected Email '%s', got '%s'", user.Email, decoded.Email)
	}

	if len(decoded.Roles) != len(user.Roles) {
		t.Errorf("Expected %d roles, got %d", len(user.Roles), len(decoded.Roles))
	}

	if decoded.IsActive != user.IsActive {
		t.Errorf("Expected IsActive %v, got %v", user.IsActive, decoded.IsActive)
	}
}

func TestRespondWithJSONArray(t *testing.T) {
	data := []map[string]string{
		{"id": "1", "name": "Item 1"},
		{"id": "2", "name": "Item 2"},
		{"id": "3", "name": "Item 3"},
	}

	recorder := httptest.NewRecorder()
	RespondWithJSON(recorder, http.StatusOK, data)

	var decoded []map[string]string
	err := json.Unmarshal(recorder.Body.Bytes(), &decoded)
	if err != nil {
		t.Fatalf("Failed to decode response: %v", err)
	}

	if len(decoded) != len(data) {
		t.Errorf("Expected %d items, got %d", len(data), len(decoded))
	}
}

func TestResponseHeadersSet(t *testing.T) {
	recorder := httptest.NewRecorder()
	RespondWithJSON(recorder, http.StatusOK, map[string]string{"test": "data"})

	// Verify Content-Type is set
	contentType := recorder.Header().Get("Content-Type")
	if contentType == "" {
		t.Error("Content-Type header should be set")
	}

	if contentType != "application/json" {
		t.Errorf("Expected Content-Type 'application/json', got '%s'", contentType)
	}
}

func BenchmarkRespondWithError(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		recorder := httptest.NewRecorder()
		RespondWithError(recorder, http.StatusBadRequest, "Test error")
	}
}

func BenchmarkRespondWithMessage(b *testing.B) {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		recorder := httptest.NewRecorder()
		RespondWithMessage(recorder, "Test message")
	}
}

func BenchmarkRespondWithJSON(b *testing.B) {
	data := map[string]interface{}{
		"id":    123,
		"name":  "Test",
		"email": "test@example.com",
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		recorder := httptest.NewRecorder()
		RespondWithJSON(recorder, http.StatusOK, data)
	}
}
