package main

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

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

	"git.agentview.dev/profit/golangLAKEHOUSE/internal/catalogd"
	"git.agentview.dev/profit/golangLAKEHOUSE/internal/storeclient"
)

// Closes R-005 for catalogd: cmd-level tests for route mounting,
// body-cap rejection, malformed JSON handling, and the decode-error
// paths in handleRegister. Deeper Registry semantics live in
// internal/catalogd/registry_test.go.

func newTestHandlers(t *testing.T) (*handlers, *httptest.Server) {
	t.Helper()
	// Stub storaged so the registry can hydrate (it needs nothing
	// initially). Empty server = 404 on any GET; that's fine for
	// these tests because we don't exercise storaged paths here.
	stub := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusNotFound)
	}))
	t.Cleanup(stub.Close)

	store := storeclient.New(stub.URL)
	reg := catalogd.NewRegistry(store)
	return newHandlers(reg), stub
}

func mountedRouter(h *handlers) chi.Router {
	r := chi.NewRouter()
	h.register(r)
	return r
}

func TestRoutesMounted(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	want := map[string]string{
		"POST /catalog/register":     "register endpoint",
		"GET /catalog/manifest/*":    "manifest endpoint",
		"GET /catalog/list":          "list endpoint",
	}
	got := map[string]bool{}
	chi.Walk(r, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
		got[method+" "+route] = true
		return nil
	})
	for sig := range want {
		if !got[sig] {
			t.Errorf("expected route %q mounted; got %v", sig, got)
		}
	}
}

func TestHandleRegister_BodyTooLarge(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	srv := httptest.NewServer(r)
	defer srv.Close()

	// 5 MiB body — over the 4 MiB cap.
	big := bytes.Repeat([]byte("x"), 5<<20)
	resp, err := http.Post(srv.URL+"/catalog/register", "application/json", bytes.NewReader(big))
	if err != nil {
		t.Fatalf("POST: %v", err)
	}
	defer resp.Body.Close()
	// MaxBytesReader trips during JSON decode → 400 with "body too large"
	// in the message, OR 413 if Content-Length up-front cap is added.
	// Today the path returns 400 via decode error; lock that contract.
	if resp.StatusCode < 400 || resp.StatusCode >= 500 {
		t.Errorf("expected 4xx on oversize body, got %d", resp.StatusCode)
	}
}

func TestHandleRegister_MalformedJSON(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	srv := httptest.NewServer(r)
	defer srv.Close()

	resp, err := http.Post(srv.URL+"/catalog/register",
		"application/json", strings.NewReader("not json"))
	if err != nil {
		t.Fatalf("POST: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusBadRequest {
		t.Errorf("expected 400 on malformed JSON, got %d", resp.StatusCode)
	}
}

func TestHandleRegister_EmptyName_400(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	srv := httptest.NewServer(r)
	defer srv.Close()

	body := `{"name":"","schema_fingerprint":"sha256:x","objects":[{"key":"k","size":1}]}`
	resp, err := http.Post(srv.URL+"/catalog/register", "application/json", strings.NewReader(body))
	if err != nil {
		t.Fatalf("POST: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusBadRequest {
		t.Errorf("expected 400 on empty name, got %d", resp.StatusCode)
	}
}

func TestHandleGetManifest_404(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	srv := httptest.NewServer(r)
	defer srv.Close()

	resp, err := http.Get(srv.URL + "/catalog/manifest/nonexistent")
	if err != nil {
		t.Fatalf("GET: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusNotFound {
		t.Errorf("expected 404 for missing manifest, got %d", resp.StatusCode)
	}
}

func TestHandleList_EmptyShape(t *testing.T) {
	h, _ := newTestHandlers(t)
	r := mountedRouter(h)
	srv := httptest.NewServer(r)
	defer srv.Close()

	resp, err := http.Get(srv.URL + "/catalog/list")
	if err != nil {
		t.Fatalf("GET: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		t.Errorf("expected 200, got %d", resp.StatusCode)
	}
	if ct := resp.Header.Get("Content-Type"); !strings.HasPrefix(ct, "application/json") {
		t.Errorf("Content-Type = %q, want application/json", ct)
	}
}
