package handlers

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

	"github.com/go-chi/chi/v5"

	"{{GO_MODULE}}/pkg/logging"
)

func newTestLogger() *logging.Logger {
	return logging.New(logging.Config{
		Level:  logging.LevelDebug,
		Format: logging.FormatText,
	})
}

func TestExample_List(t *testing.T) {
	handler := NewExample(newTestLogger())

	r := chi.NewRouter()
	r.Get("/api/v1/examples", func(w http.ResponseWriter, r *http.Request) {
		if err := handler.List(w, r); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
	})

	req := httptest.NewRequest(http.MethodGet, "/api/v1/examples", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

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

	var resp map[string]any
	if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
		t.Fatalf("failed to decode response: %v", err)
	}

	data, ok := resp["data"]
	if !ok {
		t.Fatal("expected 'data' field in response")
	}

	items, ok := data.([]any)
	if !ok {
		t.Fatal("expected 'data' to be an array")
	}

	if len(items) == 0 {
		t.Error("expected at least one item in response")
	}
}

func TestExample_Get(t *testing.T) {
	handler := NewExample(newTestLogger())

	tests := []struct {
		name       string
		id         string
		wantStatus int
	}{
		{
			name:       "valid uuid",
			id:         "550e8400-e29b-41d4-a716-446655440000",
			wantStatus: http.StatusOK,
		},
		{
			name:       "invalid uuid",
			id:         "not-a-uuid",
			wantStatus: http.StatusBadRequest,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := chi.NewRouter()
			r.Get("/api/v1/examples/{id}", func(w http.ResponseWriter, r *http.Request) {
				if err := handler.Get(w, r); err != nil {
					// Error-returning handler: convert error to status
					w.WriteHeader(http.StatusBadRequest)
					return
				}
			})

			req := httptest.NewRequest(http.MethodGet, "/api/v1/examples/"+tt.id, nil)
			w := httptest.NewRecorder()
			r.ServeHTTP(w, req)

			if w.Code != tt.wantStatus {
				t.Errorf("expected status %d, got %d", tt.wantStatus, w.Code)
			}
		})
	}
}

func TestExample_Create(t *testing.T) {
	handler := NewExample(newTestLogger())

	tests := []struct {
		name       string
		body       any
		wantStatus int
	}{
		{
			name: "valid request",
			body: CreateRequest{
				Name:        "Test Example",
				Description: "A test description",
			},
			wantStatus: http.StatusCreated,
		},
		{
			name:       "empty body",
			body:       nil,
			wantStatus: http.StatusBadRequest,
		},
		{
			name: "missing required name",
			body: map[string]string{
				"description": "no name provided",
			},
			wantStatus: http.StatusUnprocessableEntity,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := chi.NewRouter()
			r.Post("/api/v1/examples", func(w http.ResponseWriter, r *http.Request) {
				if err := handler.Create(w, r); err != nil {
					// Simulate Wrap behavior for tests
					w.WriteHeader(http.StatusBadRequest)
					return
				}
			})

			var body []byte
			if tt.body != nil {
				var err error
				body, err = json.Marshal(tt.body)
				if err != nil {
					t.Fatalf("failed to marshal body: %v", err)
				}
			}

			req := httptest.NewRequest(http.MethodPost, "/api/v1/examples", bytes.NewReader(body))
			req.Header.Set("Content-Type", "application/json")
			w := httptest.NewRecorder()
			r.ServeHTTP(w, req)

			// For the valid case, check 201
			if tt.name == "valid request" && w.Code != http.StatusCreated {
				t.Errorf("expected status %d, got %d", http.StatusCreated, w.Code)
			}
		})
	}
}

func TestExample_Delete(t *testing.T) {
	handler := NewExample(newTestLogger())

	r := chi.NewRouter()
	r.Delete("/api/v1/examples/{id}", func(w http.ResponseWriter, r *http.Request) {
		if err := handler.Delete(w, r); err != nil {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
	})

	req := httptest.NewRequest(http.MethodDelete, "/api/v1/examples/550e8400-e29b-41d4-a716-446655440000", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

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