package engineer

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

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
	engent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/engineer"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/apiserver"
	"github.com/tidbcloud/aisupport/services/ticket-manager/internal/app"
	engsvcapi "github.com/tidbcloud/aisupport/services/ticket-manager/internal/services/engineer/api"
	"go.uber.org/mock/gomock"
)

func TestListEngineersBindsFiltersAndPagination(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	service := engsvcapi.NewMockService(ctrl)
	service.EXPECT().ListEngineers(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, param engsvcapi.ListEngineersParam) ([]*engent.Engineer, int64, error) {
		require.False(t, *param.DispatchEnabledIs)
		require.Equal(t, "alice", param.Keyword)
		require.Equal(t, engsvcapi.PageParam{Page: 2, PageSize: 10}, param.Page)
		return []*engent.Engineer{{Email: "alice@example.com"}}, 1, nil
	})
	router := newTestRouter(t, &Handler{Engineer: service})

	request := httptest.NewRequest(http.MethodGet, "/engineers?page=2&page_size=10&enabled=false&keyword=alice", nil)
	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)

	require.Equal(t, http.StatusOK, response.Code)
	var body map[string]any
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &body))
	require.Equal(t, float64(1), body["total"])
	require.Len(t, body["engineers"], 1)
	engineer := body["engineers"].([]any)[0].(map[string]any)
	require.Equal(t, "alice@example.com", engineer["email"])
	_, ok := engineer["id"]
	require.False(t, ok)
	_, ok = engineer["name"]
	require.False(t, ok)
	_, ok = engineer["groups"]
	require.False(t, ok)
	_, ok = engineer["source_active"]
	require.False(t, ok)
	_, ok = engineer["source_synced_at"]
	require.False(t, ok)
}

func TestSyncEngineersReturnsCounts(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	service := engsvcapi.NewMockService(ctrl)
	service.EXPECT().SyncEngineers(gomock.Any()).Return(&engsvcapi.SyncResult{
		UsersScanned:     2,
		EngineersCreated: 3,
		EngineersUpdated: 4,
		EngineersDeleted: 6,
	}, nil)
	router := newTestRouter(t, &Handler{Engineer: service})
	response := httptest.NewRecorder()
	router.ServeHTTP(response, httptest.NewRequest(http.MethodPost, "/engineers/sync", nil))

	require.Equal(t, http.StatusOK, response.Code)
	var body map[string]any
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &body))
	require.Equal(t, float64(6), body["engineers_deleted"])
	_, ok := body["memberships_deactivated"]
	require.False(t, ok)
}

func TestEngineerMutatingRoutesAreNotRegistered(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	router := newTestRouter(t, &Handler{Engineer: engsvcapi.NewMockService(ctrl)})
	for _, request := range []*http.Request{
		httptest.NewRequest(http.MethodPost, "/engineers", nil),
		httptest.NewRequest(http.MethodPut, "/engineers/alice@example.com", nil),
		httptest.NewRequest(http.MethodDelete, "/engineers/alice@example.com", nil),
	} {
		response := httptest.NewRecorder()
		router.ServeHTTP(response, request)
		require.Equal(t, http.StatusNotFound, response.Code)
	}
}

func newTestRouter(t *testing.T, handler *Handler) *gin.Engine {
	t.Helper()
	gin.SetMode(gin.TestMode)
	router := gin.New()
	require.NoError(t, handler.Register(map[apiserver.RoutePath]gin.IRouter{app.APIRoutePath: router}))
	return router
}
