package catalog

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

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

// withRouteCtx injects a chi route context so handler.pathID can read the {id}
// param, mirroring how chi routes real requests.
func withRouteCtx(r *http.Request, id string) *http.Request {
	rcx := chi.NewRouteContext()
	rcx.URLParams.Add("id", id)
	return r.WithContext(context.WithValue(r.Context(), chi.RouteCtxKey, rcx))
}

// Repro for the reported PUT /api/v1/categories/11 -> 400: a partial update
// that omits `name` (e.g. B2B "toggle visibility" body {"is_active":false})
// used to hit "name is required" -> respondErr default -> 400. It must now
// preserve the existing name and succeed (200).
func TestUpdateCategoryHandler_PartialUpdateNoNameReturns200(t *testing.T) {
	st := newFakeStore() // categories[1] = {Name: "Sembako"}
	svc := NewService(st, &fakeSeq{}, time.UTC)
	h := NewHandler(svc)

	body := `{"is_active":false}` // omits name and description
	req := httptest.NewRequest(http.MethodPut, "/categories/1", strings.NewReader(body))
	req = withRouteCtx(req, "1")
	w := httptest.NewRecorder()

	h.UpdateCategory(w, req)

	if w.Code != http.StatusOK {
		t.Fatalf("PUT partial-update status = %d, want 200 (was 400 before fix)\nbody: %s", w.Code, w.Body.String())
	}
	if !strings.Contains(w.Body.String(), `"name":"Sembako"`) {
		t.Fatalf("response body did not preserve existing name: %s", w.Body.String())
	}
}

// A nonexistent category must surface as 404 (not a 400), even when name is
// omitted on the partial update.
func TestUpdateCategoryHandler_NotFoundIs404(t *testing.T) {
	svc := NewService(newFakeStore(), &fakeSeq{}, time.UTC)
	h := NewHandler(svc)

	req := httptest.NewRequest(http.MethodPut, "/categories/999", strings.NewReader(`{"is_active":false}`))
	req = withRouteCtx(req, "999")
	w := httptest.NewRecorder()

	h.UpdateCategory(w, req)

	if w.Code != http.StatusNotFound {
		t.Fatalf("PUT nonexistent status = %d, want 404 (400 would mean the old \"name is required\" bug)", w.Code)
	}
}

// DELETE /categories/{id} on an existing, removable category must not 400.
func TestDeleteCategoryHandler_RemovableReturns204(t *testing.T) {
	svc := NewService(newFakeStore(), &fakeSeq{}, time.UTC)
	h := NewHandler(svc)

	req := httptest.NewRequest(http.MethodDelete, "/categories/1", nil)
	req = withRouteCtx(req, "1")
	w := httptest.NewRecorder()

	h.DeleteCategory(w, req)

	if w.Code != http.StatusNoContent {
		t.Fatalf("DELETE status = %d, want 204 (no 400)", w.Code)
	}
}
