package agent

import (
	"context"
	"net/http"
	"net/http/httptest"
	"sync/atomic"
	"testing"

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	storeagent "github.com/tidbcloud/AgentTeam/internal/store/agent"
)

type countingAgentAuthenticator struct {
	calls     atomic.Int32
	principal storeagent.AuthenticatedAgent
	err       error
}

func (auth *countingAgentAuthenticator) AuthenticateAgent(context.Context, string, string) (storeagent.AuthenticatedAgent, error) {
	auth.calls.Add(1)
	return auth.principal, auth.err
}

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

	for _, authorization := range []string{"", "token", "Bearer", "Basic token", "Bearer a b"} {
		authorization := authorization
		t.Run(authorization, func(t *testing.T) {
			t.Parallel()

			auth := &countingAgentAuthenticator{}
			response := serveAgentRuntimePrincipalRequest(t, auth, authorization, "wsp_1", "agt_00000000000001")

			require.Equal(t, http.StatusUnauthorized, response.Code)
			require.Zero(t, auth.calls.Load())
		})
	}
}

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

	auth := &countingAgentAuthenticator{principal: storeagent.AuthenticatedAgent{
		AgentID:     "agt_000000000other",
		WorkspaceID: "wsp_1",
	}}
	response := serveAgentRuntimePrincipalRequest(t, auth, "Bearer token", "wsp_1", "agt_00000000000001")

	require.Equal(t, http.StatusUnauthorized, response.Code)
	require.Equal(t, int32(1), auth.calls.Load())
}

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

	auth := &countingAgentAuthenticator{principal: storeagent.AuthenticatedAgent{
		AgentID:     "agt_00000000000001",
		WorkspaceID: "wsp_other",
	}}
	response := serveAgentRuntimePrincipalRequest(t, auth, "Bearer token", "wsp_1", "agt_00000000000001")

	require.Equal(t, http.StatusMisdirectedRequest, response.Code)
	require.Contains(t, response.Body.String(), "workspace_owner_mismatch")
	require.Equal(t, int32(1), auth.calls.Load())
}

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

	principal := storeagent.AuthenticatedAgent{
		AgentID:     "agt_00000000000001",
		WorkspaceID: "wsp_1",
		MemberID:    "000000000001",
	}
	auth := &countingAgentAuthenticator{principal: principal}
	response := serveAgentRuntimePrincipalRequest(t, auth, "Bearer token", "wsp_1", "agt_00000000000001")

	require.Equal(t, http.StatusNoContent, response.Code)
	require.Equal(t, int32(1), auth.calls.Load())
}

func serveAgentRuntimePrincipalRequest(
	t *testing.T,
	auth AgentAuthenticator,
	authorization string,
	workspaceID string,
	agentID string,
) *httptest.ResponseRecorder {
	t.Helper()

	response := httptest.NewRecorder()
	_, router := gin.CreateTestContext(response)
	router.GET(
		"/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/test",
		RequireAgentRuntimePrincipal(auth),
		func(ctx *gin.Context) {
			principal, ok := api.AuthenticatedAgentFromContext(ctx)
			require.True(t, ok)
			require.Equal(t, agentID, principal.AgentID)
			require.Equal(t, workspaceID, principal.WorkspaceID)
			ctx.Status(http.StatusNoContent)
		},
	)
	request := httptest.NewRequest(
		http.MethodGet,
		"/api/v1/workspaces/"+workspaceID+"/agents/"+agentID+"/runtime/test",
		nil,
	)
	if authorization != "" {
		request.Header.Set("Authorization", authorization)
	}
	router.ServeHTTP(response, request)
	return response
}
