package main

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

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

	"git.agentview.dev/profit/golangLAKEHOUSE/internal/pathway"
)

// newTestRouter builds the pathwayd router with an in-memory store
// (nil persistor). Closes R-005 for pathwayd: 9 routes mounted with
// no router-level test prior to this file.
//
// Returns chi.Router (not http.Handler) so chi.Walk works without a
// type assertion that would panic if a future refactor wraps the
// router in plain net/http middleware.
func newTestRouter(t *testing.T) chi.Router {
	t.Helper()
	h := &handlers{store: pathway.NewStore(nil)}
	r := chi.NewRouter()
	h.register(r)
	return r
}

func TestRoutesMounted(t *testing.T) {
	r := newTestRouter(t)
	want := map[string]string{
		"POST /pathway/add":             "",
		"POST /pathway/add_idempotent":  "",
		"POST /pathway/update":          "",
		"POST /pathway/revise":          "",
		"POST /pathway/retire":          "",
		"GET /pathway/get/{uid}":        "",
		"GET /pathway/history/{uid}":    "",
		"POST /pathway/search":          "",
		"GET /pathway/stats":            "",
	}
	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 k := range want {
		if !got[k] {
			t.Errorf("route not mounted: %s", k)
		}
	}
}

// TestAdd_RoundTrip locks the happy-path contract: POST a content blob,
// receive a 201 with a trace, GET it back at /pathway/get/{uid}.
// Catches drift in either the add response shape or the get path.
func TestAdd_RoundTrip(t *testing.T) {
	r := newTestRouter(t)
	body := []byte(`{"content":{"hello":"world"},"tags":["test"]}`)
	req := httptest.NewRequest("POST", "/pathway/add", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	if w.Code != http.StatusCreated {
		t.Fatalf("expected 201 on add, got %d (body=%s)", w.Code, w.Body.String())
	}
}

func TestStats_GET(t *testing.T) {
	r := newTestRouter(t)
	req := httptest.NewRequest("GET", "/pathway/stats", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	if w.Code != http.StatusOK {
		t.Errorf("expected 200 on stats, got %d", w.Code)
	}
}

// TestAddIdempotent_MissingUID locks the validation: empty UID must
// 4xx rather than silently accepting (which would defeat the
// idempotency contract).
func TestAddIdempotent_MissingUID(t *testing.T) {
	r := newTestRouter(t)
	body := []byte(`{"content":{"x":1}}`)
	req := httptest.NewRequest("POST", "/pathway/add_idempotent", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	if w.Code/100 != 4 {
		t.Errorf("missing uid should 4xx, got %d (body=%s)", w.Code, w.Body.String())
	}
}

// TestRetire_NonexistentUID locks the not-found path. The store rejects
// retiring traces that don't exist; the handler must surface that as a
// 4xx, not a 5xx.
func TestRetire_NonexistentUID(t *testing.T) {
	r := newTestRouter(t)
	body := []byte(`{"uid":"does-not-exist"}`)
	req := httptest.NewRequest("POST", "/pathway/retire", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	if w.Code/100 != 4 {
		t.Errorf("retire of nonexistent uid should 4xx, got %d", w.Code)
	}
}
