package invocation

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

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

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/store/workspace"
	"github.com/tidbcloud/AgentTeam/internal/workflow/invocationmanage"
)

// TestInvocationCrossTenantSelectorHTTPMatrix proves the real Invocation HTTP
// handler projects an identical 404 not-found response for a missing Invocation
// selector and a cross-tenant/forbidden Invocation selector: same status, code,
// problem, details, and canonical fields. A caller cannot tell whether a
// protected Invocation exists in another tenant.
func TestInvocationCrossTenantSelectorHTTPMatrix(t *testing.T) {
	gin.SetMode(gin.TestMode)

	missing := invocationSelectorHTTPResponse(t, invocationmanage.RequestError{
		Kind: storeerr.ErrNotFound, Code: invocationmanage.ErrorCodeNotFound, Message: "Workflow Invocation wiv_secret is missing",
	})
	forbidden := invocationSelectorHTTPResponse(t, invocationmanage.RequestError{
		Kind: storeerr.ErrForbidden, Code: invocationmanage.ErrorCodeForbidden, Message: "Workflow Invocation wiv_secret is forbidden",
	})

	require.Equal(t, http.StatusNotFound, missing.Code)
	require.Equal(t, missing.Code, forbidden.Code)
	require.JSONEq(t, missing.Body.String(), forbidden.Body.String())
	require.Contains(t, missing.Body.String(), `"code":"invocation_not_found"`)
	require.NotContains(t, missing.Body.String(), "wiv_secret")
	require.NotContains(t, forbidden.Body.String(), "wiv_secret")
}

func invocationSelectorHTTPResponse(t *testing.T, selectorErr error) *httptest.ResponseRecorder {
	t.Helper()
	handler := NewHandler(&fakeService{err: selectorErr})
	router := gin.New()
	router.GET("/workflow/invocations/:invocationId",
		func(ctx *gin.Context) {
			api.SetAuthenticatedUser(ctx, identity.AuthenticatedUser{User: identity.User{ID: "usr_1"}})
			api.SetCurrentWorkspace(ctx, workspace.Summary{WorkspaceID: "wsp_1", MemberID: "mem_1", MemberUserID: "usr_1", WorkspaceRole: "owner"})
		},
		handleWorkspace(handler.Get),
	)

	request := httptest.NewRequest(http.MethodGet, "/workflow/invocations/wiv_secret", http.NoBody)
	recorder := httptest.NewRecorder()
	router.ServeHTTP(recorder, request)

	var body map[string]any
	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &body))
	require.Equal(t, "invocation_not_found", body["code"])
	require.Equal(t, "invocation", body["stage"])
	return recorder
}

// TestInvocationCrossTenantSelectorServiceMatrix proves the real Invocation
// classifier produces an identical api.ErrorResponse for missing and
// cross-tenant/forbidden protected Invocation selectors at the service boundary.
func TestInvocationCrossTenantSelectorServiceMatrix(t *testing.T) {
	missing, ok := classifyError(invocationmanage.RequestError{
		Kind: storeerr.ErrNotFound, Code: invocationmanage.ErrorCodeNotFound, Message: "Workflow Invocation wiv_secret is missing",
		Details: map[string]any{"invocationId": "wiv_secret"},
	})
	require.True(t, ok)
	forbidden, ok := classifyError(invocationmanage.RequestError{
		Kind: storeerr.ErrForbidden, Code: invocationmanage.ErrorCodeForbidden, Message: "Workflow Invocation wiv_secret is forbidden",
		Details: map[string]any{"invocationId": "wiv_secret"},
	})
	require.True(t, ok)

	require.Equal(t, http.StatusNotFound, missing.Status)
	require.Equal(t, http.StatusNotFound, forbidden.Status)
	require.Equal(t, missing.Code, forbidden.Code)
	require.Equal(t, "invocation_not_found", missing.Code)
	require.Equal(t, missing.Message, forbidden.Message)
	require.Nil(t, missing.Details)
	require.Nil(t, forbidden.Details)
	require.Equal(t, missing.Stage, forbidden.Stage)
	require.Equal(t, "invocation", missing.Stage)
	require.Equal(t, missing.Path, forbidden.Path)
	require.Equal(t, missing.Hint, forbidden.Hint)
	require.Equal(t, missing.Retryable, forbidden.Retryable)
	require.Equal(t, missing.Diagnostics, forbidden.Diagnostics)
	require.NotContains(t, missing.Message, "wiv_secret")
}
