package workspace

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

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

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
)

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

	router := gin.New()
	RegisterRoutes(router, Dependencies{})

	counts := workspaceRouteCounts(router)
	for _, route := range workspaceRouteKeys() {
		require.Equal(t, 1, counts[route], route)
	}
	for _, route := range machineRouteKeys() {
		require.Equal(t, 1, counts[route], route)
	}
	for _, route := range workspaceDataPlaneRouteKeys() {
		require.Equal(t, 1, counts[route], route)
	}
	for _, route := range router.Routes() {
		require.False(
			t,
			route.Path == "/api/workspaces" || strings.HasPrefix(route.Path, "/api/workspaces/"),
			"%s %s",
			route.Method,
			route.Path,
		)
	}
	for _, route := range []string{
		"GET /api/v1/me/execution-policy",
		"PATCH /api/v1/me/execution-policy",
		"GET /api/v1/workspaces/:workspaceId/machines/:machineId/execution-policy",
		"PATCH /api/v1/workspaces/:workspaceId/machines/:machineId/execution-policy",
		"POST /api/machines/connect-exchange",
	} {
		require.Zero(t, workspaceRouteCounts(router)[route], route)
	}
}

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

	router := gin.New()
	RegisterRoutes(router, Dependencies{
		AuthStore: &fakeWorkspaceService{},
		WorkspaceService: &fakeWorkspaceService{
			authenticated:  identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}},
			userWorkspaces: []workspace.Summary{},
		},
	})

	recorder := httptest.NewRecorder()
	request := httptest.NewRequest(http.MethodGet, "/api/v1/workspaces", nil)
	request.Header.Set("Authorization", "Bearer session-token")
	router.ServeHTTP(recorder, request)

	require.Equal(t, http.StatusOK, recorder.Code)
	require.JSONEq(t, `[]`, recorder.Body.String())
}

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

	router := gin.New()
	RegisterRoutes(router, Dependencies{
		WorkspaceService: &fakeWorkspaceService{exchange: dto.MachineConnectExchangeResponse{
			MachineID:   "mach_1",
			APIKey:      "api-key",
			ServerURL:   "https://agentteam.example.com",
			WorkspaceID: "wsp_1",
		}},
	})

	cases := []struct {
		path string
		body string
	}{
		{path: "/api/v1/machine-connect-exchanges", body: `{"token":"one-time"}`},
		{path: "/api/v1/machine-auth-resolutions", body: `{"apiKey":"api-key","serverUrl":"https://agentteam.example.com"}`},
	}
	for _, tt := range cases {
		tt := tt
		t.Run(tt.path, func(t *testing.T) {
			t.Parallel()
			recorder := httptest.NewRecorder()
			request := httptest.NewRequest(http.MethodPost, tt.path, bytes.NewBufferString(tt.body))
			request.Header.Set("Content-Type", "application/json")
			router.ServeHTTP(recorder, request)

			require.Equal(t, http.StatusOK, recorder.Code)
			require.JSONEq(t, `{"machineId":"mach_1","apiKey":"api-key","serverUrl":"https://agentteam.example.com","workspaceId":"wsp_1"}`, recorder.Body.String())
		})
	}
}

func workspaceRouteKeys() []string {
	return []string{
		"GET /api/v1/me/behavior-settings",
		"PATCH /api/v1/me/behavior-settings",
		"GET /api/v1/workspaces",
		"POST /api/v1/workspaces",
		"PATCH /api/v1/workspaces/:workspaceId",
		"GET /api/v1/workspaces/:workspaceId/organization",
		"GET /api/v1/workspaces/:workspaceId/members",
		"POST /api/v1/workspaces/:workspaceId/members/search",
		"POST /api/v1/workspaces/:workspaceId/members:batchLookup",
		"GET /api/v1/workspaces/:workspaceId/members/by-user/:userId/offboarding-impact",
		"GET /api/v1/workspaces/:workspaceId/members/:memberId",
		"PATCH /api/v1/workspaces/:workspaceId/members/:memberId",
		"DELETE /api/v1/workspaces/:workspaceId/members/:memberId",
		"GET /api/v1/workspaces/:workspaceId/invites",
		"POST /api/v1/workspaces/:workspaceId/invites",
		"DELETE /api/v1/workspaces/:workspaceId/invites/:inviteId",
		"GET /api/v1/workspaces/:workspaceId/usage",
	}
}

func machineRouteKeys() []string {
	return []string{
		"POST /api/v1/machine-connect-exchanges",
		"GET /api/v1/workspaces/:workspaceId/machines",
		"POST /api/v1/workspaces/:workspaceId/machines",
		"PATCH /api/v1/workspaces/:workspaceId/machines/:machineId",
		"POST /api/v1/workspaces/:workspaceId/machines/:machineId/rename",
		"DELETE /api/v1/workspaces/:workspaceId/machines/:machineId",
		"POST /api/v1/workspaces/:workspaceId/machines/:machineId/api-key-rotations",
		"POST /api/v1/workspaces/:workspaceId/machines/:machineId/connect-tokens",
		"POST /api/v1/workspaces/:workspaceId/machines/:machineId/evictions",
		"GET /api/v1/workspaces/:workspaceId/machines/:machineId/agents",
		"GET /api/v1/workspaces/:workspaceId/machines/:machineId/workspace-directories",
		"DELETE /api/v1/workspaces/:workspaceId/machines/:machineId/workspace-directories/:directoryName",
		"POST /api/v1/workspaces/:workspaceId/machines/:machineId/directories:list",
	}
}

func workspaceDataPlaneRouteKeys() []string {
	return []string{
		"GET /api/v1/workspaces/:workspaceId/providers",
		"GET /api/v1/workspaces/:workspaceId/selectable-models",
		"GET /api/v1/workspaces/:workspaceId/sidebar/channel-sections",
		"PUT /api/v1/workspaces/:workspaceId/sidebar/channel-sections",
	}
}

func workspaceRouteCounts(router *gin.Engine) map[string]int {
	counts := make(map[string]int)
	for _, route := range router.Routes() {
		counts[route.Method+" "+route.Path]++
	}
	return counts
}
