package integration

import (
	"bytes"
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

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

	pkgmiddleware "github.com/TaruLab/keluargamart-backend/pkg/middleware"
)

func TestIdempotencyMiddleware_HTTP(t *testing.T) {
	rdb := testRedis(t)
	ctx := context.Background()
	if err := rdb.FlushDB(ctx).Err(); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() { _ = rdb.FlushDB(ctx) })

	var calls int
	r := chi.NewRouter()
	r.Post("/mutate", func(w http.ResponseWriter, req *http.Request) {
		calls++
		_, _ = io.ReadAll(req.Body)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusCreated)
		_, _ = w.Write([]byte(`{"ok":true}`))
	})
	im := pkgmiddleware.NewIdempotency(rdb, pkgmiddleware.WithRequired(true))
	r.Route("/idem", func(ir chi.Router) {
		ir.With(im.Handler).Post("/mutate", func(w http.ResponseWriter, req *http.Request) {
			calls++
			_, _ = io.ReadAll(req.Body)
			w.Header().Set("Content-Type", "application/json")
			w.WriteHeader(http.StatusCreated)
			_, _ = w.Write([]byte(`{"ok":true}`))
		})
	})

	const key = "it-http-key-1"
	body := []byte(`{"amount":100}`)

	// 1. missing key -> 422
	req := httptest.NewRequest(http.MethodPost, "/idem/mutate", bytes.NewReader(body))
	rec := httptest.NewRecorder()
	r.ServeHTTP(rec, req)
	if rec.Code != http.StatusUnprocessableEntity {
		t.Errorf("missing key status = %d, want 422", rec.Code)
	}

	// 2. first call executes (201)
	req = httptest.NewRequest(http.MethodPost, "/idem/mutate", bytes.NewReader(body))
	req.Header.Set("Idempotency-Key", key)
	rec = httptest.NewRecorder()
	r.ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Fatalf("first status = %d, want 201", rec.Code)
	}

	// 3. same key + same body -> replay of the stored response (handler not run again)
	before := calls
	req = httptest.NewRequest(http.MethodPost, "/idem/mutate", bytes.NewReader(body))
	req.Header.Set("Idempotency-Key", key)
	rec = httptest.NewRecorder()
	r.ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Fatalf("replay status = %d, want 201", rec.Code)
	}
	if rec.Body.String() != `{"ok":true}` {
		t.Errorf("replay body = %q, want stored response", rec.Body.String())
	}
	if calls != before {
		t.Errorf("handler ran %d times on replay, want %d", calls, before)
	}

	// 4. same key + different body -> 409 conflict
	req = httptest.NewRequest(http.MethodPost, "/idem/mutate", bytes.NewReader([]byte(`{"amount":200}`)))
	req.Header.Set("Idempotency-Key", key)
	rec = httptest.NewRecorder()
	r.ServeHTTP(rec, req)
	if rec.Code != http.StatusConflict {
		t.Errorf("conflict status = %d, want 409", rec.Code)
	}

	// 5. plain (non-guarded) route is untouched
	req = httptest.NewRequest(http.MethodPost, "/mutate", bytes.NewReader(body))
	rec = httptest.NewRecorder()
	r.ServeHTTP(rec, req)
	if rec.Code != http.StatusCreated {
		t.Errorf("unguarded status = %d, want 201", rec.Code)
	}
}
