package handlers

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-gonic/gin"

	"switchboard-core/store"
)

// mockClusterStore implements store.ClusterStore for handler tests.
type mockClusterStore struct {
	nodes []store.ClusterNode
}

func (m *mockClusterStore) Register(_ context.Context, _, _ string) error               { return nil }
func (m *mockClusterStore) Heartbeat(_ context.Context, _ string, _ json.RawMessage) (int64, error) {
	return 1, nil
}
func (m *mockClusterStore) SweepStale(_ context.Context, _ time.Duration) (int64, error) { return 0, nil }
func (m *mockClusterStore) ListNodes(_ context.Context) ([]store.ClusterNode, error) {
	return m.nodes, nil
}
func (m *mockClusterStore) Deregister(_ context.Context, _ string) error { return nil }

func TestClusterListNodes(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mock := &mockClusterStore{
		nodes: []store.ClusterNode{
			{
				NodeID:       "node-1",
				Endpoint:     "http://node-1:8080",
				Seq:          1,
				RegisteredAt: time.Now(),
				Heartbeat:    time.Now(),
				Stats:        json.RawMessage(`{"ws_clients":3}`),
			},
			{
				NodeID:       "node-2",
				Endpoint:     "http://node-2:8080",
				Seq:          2,
				RegisteredAt: time.Now(),
				Heartbeat:    time.Now(),
				Stats:        json.RawMessage(`{"ws_clients":7}`),
			},
		},
	}

	stores := store.Stores{Cluster: mock}
	h := NewClusterHandler(stores)

	r := gin.New()
	r.GET("/api/v1/admin/cluster", h.ListNodes)

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

	if w.Code != http.StatusOK {
		t.Fatalf("status = %d, want %d", w.Code, http.StatusOK)
	}

	var resp struct {
		Data []store.ClusterNode `json:"data"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal response: %v", err)
	}
	if len(resp.Data) != 2 {
		t.Fatalf("data length = %d, want 2", len(resp.Data))
	}
	if resp.Data[0].NodeID != "node-1" {
		t.Errorf("data[0].node_id = %q, want %q", resp.Data[0].NodeID, "node-1")
	}
	if resp.Data[1].NodeID != "node-2" {
		t.Errorf("data[1].node_id = %q, want %q", resp.Data[1].NodeID, "node-2")
	}
}

func TestClusterListNodesEmpty(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mock := &mockClusterStore{nodes: nil}
	stores := store.Stores{Cluster: mock}
	h := NewClusterHandler(stores)

	r := gin.New()
	r.GET("/api/v1/admin/cluster", h.ListNodes)

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

	if w.Code != http.StatusOK {
		t.Fatalf("status = %d, want %d", w.Code, http.StatusOK)
	}

	var resp struct {
		Data []json.RawMessage `json:"data"`
	}
	if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	// Should be empty array, not null
	if resp.Data == nil {
		t.Error("data should be [], not null")
	}
}
