package api

import (
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/alertstore/pkg/directory"
)

// searchUsersOn builds the minimum server the assignee search needs and runs
// one request through it, returning the response and how many times the
// directory upstream was asked.
func searchUsersOn(t *testing.T, upstreamURL, query string) (*httptest.ResponseRecorder, *Server) {
	t.Helper()
	client, err := directory.New(directory.Config{BaseURL: upstreamURL}, nil)
	require.NoError(t, err)
	server := &Server{
		directory: client,
		logger:    slog.New(slog.NewTextHandler(io.Discard, nil)),
	}

	gin.SetMode(gin.TestMode)
	router := gin.New()
	router.GET("/users", server.searchUsers)
	resp := httptest.NewRecorder()
	router.ServeHTTP(resp, httptest.NewRequest(http.MethodGet, "/users?"+query, nil))
	return resp, server
}

func TestSearchUsersReturnsDirectoryMatches(t *testing.T) {
	var gotKeyword string
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotKeyword = r.URL.Query().Get("userKeyword")
		fmt.Fprint(w, `{"users": [{"email": "wang.kang@pingcap.com", "larkUserName": "王康"}]}`)
	}))
	t.Cleanup(upstream.Close)

	resp, _ := searchUsersOn(t, upstream.URL, "q=wang")
	require.Equal(t, http.StatusOK, resp.Code)
	require.JSONEq(t, `{"users":[{"email":"wang.kang@pingcap.com","name":"王康","en_name":"","department":""}]}`, resp.Body.String())
	require.Equal(t, "wang", gotKeyword)
}

func TestSearchUsersWithoutAKeyword(t *testing.T) {
	var calls atomic.Int32
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		calls.Add(1)
		fmt.Fprint(w, `{"users": []}`)
	}))
	t.Cleanup(upstream.Close)

	resp, _ := searchUsersOn(t, upstream.URL, "q=+++")
	require.Equal(t, http.StatusOK, resp.Code)
	require.JSONEq(t, `{"users":[]}`, resp.Body.String())
	require.Zero(t, calls.Load(), "nothing typed is nothing to search for")
}

// A directory that cannot answer costs suggestions, not the assignment: the
// field it feeds is free text, so the picker degrades instead of erroring.
func TestSearchUsersFailsOpen(t *testing.T) {
	upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "boom", http.StatusInternalServerError)
	}))
	t.Cleanup(upstream.Close)

	resp, _ := searchUsersOn(t, upstream.URL, "q=wang")
	require.Equal(t, http.StatusOK, resp.Code)
	require.JSONEq(t, `{"users":[]}`, resp.Body.String())
}

func TestSearchUsersWithoutAConfiguredDirectory(t *testing.T) {
	resp, server := searchUsersOn(t, "", "q=wang")
	require.False(t, server.directory.Enabled())
	require.Equal(t, http.StatusOK, resp.Code)
	require.JSONEq(t, `{"users":[]}`, resp.Body.String())
}
