package agent

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

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

	workflowrun "github.com/tidbcloud/AgentTeam/internal/api/workflow/run"
	storeagent "github.com/tidbcloud/AgentTeam/internal/store/agent"
	workflowrunstore "github.com/tidbcloud/AgentTeam/internal/store/workflow/run"
	storeworkspace "github.com/tidbcloud/AgentTeam/internal/store/workspace"
	invocationlang "github.com/tidbcloud/AgentTeam/internal/workflow/invocation"
	manage "github.com/tidbcloud/AgentTeam/internal/workflow/invocationmanage"
	"github.com/tidbcloud/AgentTeam/internal/workflow/publicerr"
	runkernel "github.com/tidbcloud/AgentTeam/internal/workflow/run"
	"github.com/tidbcloud/AgentTeam/internal/workflow/runartifact"
	runruntime "github.com/tidbcloud/AgentTeam/internal/workflow/orchestrator"
)

type publicRouteRunService struct{}

func (publicRouteRunService) ListRuns(context.Context, workflowrun.ListRunsInput) (workflowrun.RunPage, error) {
	return workflowrun.RunPage{}, nil
}

func (publicRouteRunService) GetRun(context.Context, string, string) (workflowrunstore.Run, error) {
	return workflowrunstore.Run{}, nil
}

func (publicRouteRunService) ListActivations(context.Context, workflowrun.ListChildrenInput) (workflowrun.ActivationPage, error) {
	return workflowrun.ActivationPage{}, nil
}

func (publicRouteRunService) ListEffects(context.Context, workflowrun.ListChildrenInput) (workflowrun.EffectPage, error) {
	return workflowrun.EffectPage{}, nil
}

func (publicRouteRunService) ListEffectQueueContexts(context.Context, string, string) (map[string]workflowrunstore.EffectQueueContext, error) {
	return map[string]workflowrunstore.EffectQueueContext{}, nil
}

func (publicRouteRunService) ListEvents(context.Context, workflowrun.ListEventsInput) (workflowrun.EventPage, error) {
	return workflowrun.EventPage{}, nil
}

func (publicRouteRunService) GetEvent(context.Context, string, string, int64) (workflowrunstore.Event, error) {
	return workflowrunstore.Event{}, nil
}

func (publicRouteRunService) Cancel(context.Context, workflowrun.CancelInput) (workflowrun.CancelResult, error) {
	return workflowrun.CancelResult{}, nil
}

func (service publicRouteRunService) ReadArtifactPageForAgent(context.Context, workflowrun.ArtifactReadInput, workflowrun.AgentPrincipal) (workflowrun.ArtifactPage, error) {
	return workflowrun.ArtifactPage{}, fmt.Errorf("artifact reader is not configured")
}

func (service publicRouteRunService) ListRunsForAgent(ctx context.Context, input workflowrun.ListRunsInput, _ workflowrun.AgentPrincipal) (workflowrun.RunPage, error) {
	return service.ListRuns(ctx, input)
}
func (service publicRouteRunService) GetRunForAgent(ctx context.Context, workspaceID, runID string, _ workflowrun.AgentPrincipal) (workflowrunstore.Run, error) {
	return service.GetRun(ctx, workspaceID, runID)
}
func (service publicRouteRunService) ListActivationsForAgent(ctx context.Context, input workflowrun.ListChildrenInput, _ workflowrun.AgentPrincipal) (workflowrun.ActivationPage, error) {
	return service.ListActivations(ctx, input)
}
func (service publicRouteRunService) ListEffectsForAgent(ctx context.Context, input workflowrun.ListChildrenInput, _ workflowrun.AgentPrincipal) (workflowrun.EffectPage, error) {
	return service.ListEffects(ctx, input)
}
func (service publicRouteRunService) ListEventsForAgent(ctx context.Context, input workflowrun.ListEventsInput, _ workflowrun.AgentPrincipal) (workflowrun.EventPage, error) {
	return service.ListEvents(ctx, input)
}
func (service publicRouteRunService) GetEventForAgent(ctx context.Context, workspaceID, runID string, sequence int64, _ workflowrun.AgentPrincipal) (workflowrunstore.Event, error) {
	return service.GetEvent(ctx, workspaceID, runID, sequence)
}
func (service publicRouteRunService) CancelForAgent(ctx context.Context, input workflowrun.CancelInput, _ workflowrun.AgentPrincipal) (workflowrun.CancelResult, error) {
	return service.Cancel(ctx, input)
}

type capturingPublicRouteRunService struct {
	publicRouteRunService
	listInput workflowrun.ListRunsInput
}

type artifactPublicRouteRunService struct {
	publicRouteRunService
	ref     runartifact.Ref
	content []byte
	called  bool
	nested  bool
}

func (service *artifactPublicRouteRunService) GetRun(context.Context, string, string) (workflowrunstore.Run, error) {
	value := any(runartifact.Marker(service.ref))
	if service.nested {
		value = map[string]any{"dependencies": map[string]any{"join": map[string]any{"value": value}}}
	}
	output, _ := json.Marshal(value)
	return workflowrunstore.Run{ID: service.ref.RunID, WorkspaceID: service.ref.WorkspaceID, OutputJSON: output}, nil
}

func (service *artifactPublicRouteRunService) GetRunForAgent(_ context.Context, _, _ string, _ workflowrun.AgentPrincipal) (workflowrunstore.Run, error) {
	return service.GetRun(context.Background(), service.ref.WorkspaceID, service.ref.RunID)
}

func (service *artifactPublicRouteRunService) ReadArtifactPage(_ context.Context, input workflowrun.ArtifactReadInput) (workflowrun.ArtifactPage, error) {
	service.called = true
	selected, err := runartifact.SelectPath(service.content, input.Path)
	if err != nil {
		return workflowrun.ArtifactPage{}, err
	}
	offset := 0
	if input.Cursor != "" {
		_, _ = fmt.Sscanf(input.Cursor, "c:%d", &offset)
	}
	end := offset + input.Limit
	if end > len(selected) {
		end = len(selected)
	}
	next := ""
	if end < len(selected) {
		next = fmt.Sprintf("c:%d", end)
	}
	return workflowrun.ArtifactPage{Reference: input.Reference, Section: input.Section, Path: input.Path, Bytes: selected[offset:end], Offset: offset, TotalBytes: len(selected), NextCursor: next}, nil
}

func (service *artifactPublicRouteRunService) ReadArtifactPageForAgent(ctx context.Context, input workflowrun.ArtifactReadInput, _ workflowrun.AgentPrincipal) (workflowrun.ArtifactPage, error) {
	return service.ReadArtifactPage(ctx, input)
}

func (service *capturingPublicRouteRunService) ListRuns(_ context.Context, input workflowrun.ListRunsInput) (workflowrun.RunPage, error) {
	service.listInput = input
	return workflowrun.RunPage{}, nil
}

func (service *capturingPublicRouteRunService) ListRunsForAgent(_ context.Context, input workflowrun.ListRunsInput, _ workflowrun.AgentPrincipal) (workflowrun.RunPage, error) {
	service.listInput = input
	return workflowrun.RunPage{}, nil
}

type publicRouteStepRuntime struct {
	command runruntime.CallbackCommand
	list    runruntime.CallbackListCommand
}

type publicRouteRunStarter struct {
	manual *manage.ManualInput
}

func (starter *publicRouteRunStarter) ReplayManual(context.Context, manage.ReplayInput) (invocationlang.AcceptResult, bool, error) {
	return invocationlang.AcceptResult{}, false, nil
}

func (starter *publicRouteRunStarter) InvokeManual(_ context.Context, input manage.ManualInput) (invocationlang.AcceptResult, error) {
	starter.manual = &input
	return invocationlang.AcceptResult{
		Invocation: invocationlang.Invocation{
			PlanID: "wpl_1", PlanVersionID: "wpv_1",
		},
		Run: invocationlang.Run{ID: "wri_1", Status: "queued"},
	}, nil
}

type publicRouteRunOwner struct{}

func (publicRouteRunOwner) AgentManagedByUser(context.Context, string, string) (string, error) {
	return "usr_00000000000001", nil
}

type publicRouteRunWorkspace struct{}

func (publicRouteRunWorkspace) GetWorkspaceForUser(context.Context, string, string) (storeworkspace.Summary, error) {
	return storeworkspace.Summary{
		WorkspaceID: "wsp_test", MemberUserID: "usr_00000000000001", MemberID: "0000000owner", WorkspaceRole: "owner",
	}, nil
}

func (runtime *publicRouteStepRuntime) ExecuteCallback(_ context.Context, command runruntime.CallbackCommand) (runruntime.CallbackResult, error) {
	runtime.command = command
	return runruntime.CallbackResult{
		RunID: command.RunID, CallbackID: command.CallbackID, Status: "waiting", RunStatus: "running",
	}, nil
}

func (runtime *publicRouteStepRuntime) ListCallbacks(_ context.Context, command runruntime.CallbackListCommand) (runruntime.CallbackListResult, error) {
	runtime.list = command
	return runruntime.CallbackListResult{Steps: []runruntime.CallbackResult{}}, nil
}

func TestWorkflowPublicRunAndStepRoutesExcludeLegacyObjects(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	auth := fakeAgentAuthenticator{agent: storeagent.AuthenticatedAgent{
		AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "00000000test",
	}}
	registerAgentWorkflowRunRoutes(
		router, "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime", auth,
		publicRouteRunService{}, nil, nil, nil,
	)
	registerAgentWorkflowStepRoutes(
		router, "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime", auth,
		&publicRouteStepRuntime{}, nil,
	)

	var paths []string
	for _, route := range router.Routes() {
		paths = append(paths, route.Path)
	}
	require.ElementsMatch(t, []string{
		"/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/workflow/run",
		"/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/workflow/step",
	}, paths)
	require.NotContains(t, paths, "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/workflow/invocation")
	require.NotContains(t, paths, "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime/workflow/run/callbacks")
}

func TestWorkflowRunCommandsUseBusinessNamesOnly(t *testing.T) {
	router := workflowPublicTestRouter(t, publicRouteRunService{}, nil)
	start := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{
		"action": "start", "planId": "wpl_1", "input": map[string]any{},
	})
	require.Equal(t, http.StatusServiceUnavailable, start.Code, start.Body.String())
	for _, action := range []string{"list", "show", "logs", "cancel"} {
		input := map[string]any{"action": action}
		if action != "list" {
			input["runId"] = "wri_1"
		}
		response := postWorkflowPublicCommand(t, router, "/workflow/run", input)
		require.NotEqual(t, http.StatusNotFound, response.Code, action+": "+response.Body.String())
	}
	for _, action := range []string{"get", "invoke", "list_activations", "list_effects", "list_events"} {
		response := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{"action": action, "runId": "wri_1"})
		require.Equal(t, http.StatusNotFound, response.Code, action+": "+response.Body.String())
	}
}

func TestWorkflowRunShowConsumesBoundedJoinArtifactPages(t *testing.T) {
	content := []byte(`{"items":"` + strings.Repeat("x", 70000) + `"}`)
	draft, err := runartifact.NewDraftForKind(runartifact.KindJoinAggregate, "wsp_test", "wri_artifact", "join", "act_join", content)
	require.NoError(t, err)
	require.NoError(t, draft.Ref.Validate())
	service := &artifactPublicRouteRunService{ref: draft.Ref, content: content}
	router := workflowPublicTestRouter(t, service, nil)
	first := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{
		"action": "show", "runId": draft.Ref.RunID, "section": "output", "path": "$.items", "limit": 1024,
	})
	require.Equal(t, http.StatusOK, first.Code, first.Body.String())
	require.True(t, service.called)
	var firstBody struct {
		Payload struct {
			Value      string `json:"value"`
			NextCursor string `json:"nextCursor"`
		} `json:"payload"`
	}
	require.NoError(t, json.Unmarshal(first.Body.Bytes(), &firstBody))
	require.Equal(t, (`"` + strings.Repeat("x", 1023) + `"`)[:1024], firstBody.Payload.Value)
	require.NotEmpty(t, firstBody.Payload.NextCursor)
	second := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{
		"action": "show", "runId": draft.Ref.RunID, "section": "output", "path": "$.items", "cursor": firstBody.Payload.NextCursor, "limit": 1024,
	})
	require.Equal(t, http.StatusOK, second.Code, second.Body.String())
	require.NotContains(t, second.Body.String(), strings.Repeat("x", 70000), "the Agent envelope must stay bounded")
}

func TestWorkflowRunShowResolvesNestedArtifactPath(t *testing.T) {
	content := []byte(`{"items":["exact",2]}`)
	draft, err := runartifact.NewDraftForKind(runartifact.KindJoinAggregate, "wsp_test", "wri_nested", "join", "act_join", content)
	require.NoError(t, err)
	service := &artifactPublicRouteRunService{ref: draft.Ref, content: content, nested: true}
	router := workflowPublicTestRouter(t, service, nil)
	response := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{
		"action": "show", "runId": draft.Ref.RunID, "section": "output", "path": "$.dependencies.join.value.items", "limit": 1024,
	})
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	var body struct {
		Payload struct {
			Value string `json:"value"`
		} `json:"payload"`
	}
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &body))
	require.Equal(t, `["exact",2]`, body.Payload.Value)
}

func TestWorkflowRunListPassesPlanIDFilter(t *testing.T) {
	service := &capturingPublicRouteRunService{}
	router := workflowPublicTestRouter(t, service, nil)
	response := postWorkflowPublicCommand(t, router, "/workflow/run", map[string]any{
		"action": "list", "planId": " pln_1 ", "status": " running ", "limit": 12,
	})

	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, workflowrun.ListRunsInput{
		WorkspaceID: "wsp_test", PlanID: "pln_1", Status: "running", Limit: 12,
	}, service.listInput)
}

func TestWorkflowRunStartUsesInternalAcceptanceService(t *testing.T) {
	gin.SetMode(gin.TestMode)
	router := gin.New()
	auth := fakeAgentAuthenticator{agent: storeagent.AuthenticatedAgent{
		AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "00000000test",
	}}
	starter := &publicRouteRunStarter{}
	registerAgentWorkflowRunRoutes(
		router, "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime", auth,
		publicRouteRunService{}, starter, publicRouteRunOwner{}, publicRouteRunWorkspace{},
	)
	commandID := "cli:0123456789abcdef0123456789abcdef"
	input := map[string]any{"action": "start", "planId": "wpl_1", "input": map[string]any{"key": "value"}}
	envelope := workflowCLIEnvelope{
		Context: workflowCLIContext{AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", CommandID: commandID},
		Input:   mustWorkflowPublicJSON(t, input),
	}
	body, err := json.Marshal(envelope)
	require.NoError(t, err)
	request := httptest.NewRequest(
		http.MethodPost, "/api/v1/workspaces/wsp_test/agents/agt_0000000000test/runtime/workflow/run", bytes.NewReader(body),
	)
	request.Header.Set("Authorization", "Bearer test-token")
	request.Header.Set(
		workflowCLIIdempotencyHeader,
		workflowCLIIdempotencyKey("agt_0000000000test", "wsp_test", workflowCLIObjectRun, "start", commandID),
	)
	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)

	require.Equal(t, http.StatusCreated, response.Code, response.Body.String())
	require.NotNil(t, starter.manual)
	require.Equal(t, "0000000owner", starter.manual.Principal.MemberID)
	require.Equal(t, "agt_0000000000test", starter.manual.InvokedBy.ID)
	require.Contains(t, response.Body.String(), `"runId":"wri_1"`)
	require.NotContains(t, response.Body.String(), "invocation")
}

func TestWorkflowStepMapsPublicGetToRuntimeAction(t *testing.T) {
	runtime := &publicRouteStepRuntime{}
	router := workflowPublicTestRouter(t, publicRouteRunService{}, runtime)
	response := postWorkflowStepCommand(t, router, map[string]any{
		"action": "get", "workflowRunId": "wri_1", "workflowStepId": "wst_1",
	})
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, runruntime.CallbackActionGetCurrent, runtime.command.Action)
	require.Equal(t, "wst_1", runtime.command.CallbackID)
	require.Contains(t, response.Body.String(), `"stepId":"wst_1"`)
	require.NotContains(t, response.Body.String(), "callback")

	for _, action := range []string{"current", "get_current", "callback"} {
		response = postWorkflowStepCommand(t, router, map[string]any{
			"action": action, "workflowRunId": "wri_1", "workflowStepId": "wst_1",
		})
		require.Equal(t, http.StatusBadRequest, response.Code, response.Body.String())
	}
}

func TestWorkflowStepListsCallbacksForAuthenticatedAgent(t *testing.T) {
	runtime := &publicRouteStepRuntime{}
	router := workflowPublicTestRouter(t, publicRouteRunService{}, runtime)
	response := postWorkflowStepCommand(t, router, map[string]any{
		"action": "list", "limit": 25, "cursor": "wfx_8bb9a163e8634564a44defb391b2fa5d",
	})
	require.Equal(t, http.StatusOK, response.Code, response.Body.String())
	require.Equal(t, runruntime.CallbackListCommand{
		WorkspaceID: "wsp_test", AgentID: "agt_0000000000test", MemberID: "00000000test", Limit: 25, Cursor: "wfx_8bb9a163e8634564a44defb391b2fa5d",
	}, runtime.list)
	require.JSONEq(t, `{"steps":[]}`, response.Body.String())
}

func postWorkflowStepCommand(t *testing.T, router *gin.Engine, input map[string]any) *httptest.ResponseRecorder {
	t.Helper()
	body, err := json.Marshal(input)
	require.NoError(t, err)
	request := httptest.NewRequest(
		http.MethodPost,
		"/api/v1/workspaces/wsp_test/agents/agt_0000000000test/runtime/workflow/step",
		bytes.NewReader(body),
	)
	request.Header.Set("Authorization", "Bearer test-token")
	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)
	return response
}

func workflowPublicTestRouter(
	t *testing.T,
	runs workflowrun.Service,
	steps runruntime.CallbackRuntime,
) *gin.Engine {
	t.Helper()
	gin.SetMode(gin.TestMode)
	router := gin.New()
	auth := fakeAgentAuthenticator{agent: storeagent.AuthenticatedAgent{
		AgentID: "agt_0000000000test", WorkspaceID: "wsp_test", MemberID: "00000000test",
	}}
	base := "/api/v1/workspaces/:workspaceId/agents/:agentId/runtime"
	registerAgentWorkflowRunRoutes(router, base, auth, runs, nil, nil, nil)
	if steps != nil {
		registerAgentWorkflowStepRoutes(router, base, auth, steps, nil)
	}
	return router
}

func postWorkflowPublicCommand(t *testing.T, router *gin.Engine, suffix string, input map[string]any) *httptest.ResponseRecorder {
	t.Helper()
	envelope := workflowCLIEnvelope{
		Context: workflowCLIContext{
			AgentID: "agt_0000000000test", WorkspaceID: "wsp_test",
			CommandID: "cli:0123456789abcdef0123456789abcdef",
		},
		Input: mustWorkflowPublicJSON(t, input),
	}
	body, err := json.Marshal(envelope)
	require.NoError(t, err)
	request := httptest.NewRequest(
		http.MethodPost,
		"/api/v1/workspaces/wsp_test/agents/agt_0000000000test/runtime"+suffix,
		bytes.NewReader(body),
	)
	request.Header.Set("Authorization", "Bearer test-token")
	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)
	return response
}

func mustWorkflowPublicJSON(t *testing.T, value any) json.RawMessage {
	t.Helper()
	encoded, err := json.Marshal(value)
	require.NoError(t, err)
	return encoded
}

// realHandlerStepRuntime returns scripted callback failures so a regression can
// drive the real gin handler and ErrorWithContract writer (no fabricated
// public body) end to end.
type realHandlerStepRuntime struct {
	err error
}

func (runtime realHandlerStepRuntime) ExecuteCallback(_ context.Context, _ runruntime.CallbackCommand) (runruntime.CallbackResult, error) {
	return runruntime.CallbackResult{}, runtime.err
}

func (realHandlerStepRuntime) ListCallbacks(context.Context, runruntime.CallbackListCommand) (runruntime.CallbackListResult, error) {
	return runruntime.CallbackListResult{Steps: []runruntime.CallbackResult{}}, nil
}

// TestWorkflowStepRealHandlerWritesPublicRetryableAndDiagnostics proves the
// real gin handler + ErrorWithContract writer emit the public contract
// fields (top-level retryable, stage, path, typed diagnostics) for the step
// callback validation failure and the delivery race, without a fabricated
// resolved body.
func TestWorkflowStepRealHandlerWritesPublicRetryableAndDiagnostics(t *testing.T) {
	gin.SetMode(gin.TestMode)
	index := 2
	diagnostics := []publicerr.Diagnostic{{
		Severity: publicerr.SeverityError,
		Code:     "callback.work_items.key_invalid",
		Path:     "callback.workItems[2].key",
		Field:    "key",
		Index:    &index,
		Message:  "callback.workItems[2].key must match ^[a-z][a-z0-9_-]{0,255}$",
	}}
	validationErr := callbackValidationFailure(
		runruntime.CallbackValidationCodeWorkItems,
		diagnostics,
		runkernel.NewTestValidationErrorForTest(
			runkernel.TestValidationCodeWorkItemsKeyInvalid,
			"callback.workItems[2].key",
			"key",
			2,
			"callback.workItems[2].key must match ^[a-z][a-z0-9_-]{0,255}$",
		),
	)

	tests := []struct {
		name          string
		stepErr       error
		wantStatus    int
		wantCode      string
		wantStage     string
		wantPath      string
		wantRetryable bool
		wantDiags     int
	}{
		{
			name:       "callback validation failure carries stage path and diagnostics",
			stepErr:    validationErr,
			wantStatus: http.StatusBadRequest,
			wantCode:   runruntime.CallbackValidationCodeWorkItems,
			wantStage:  "run", wantPath: "callback.workItems[2].key",
			wantDiags: 1,
		},
		{
			name:       "delivery race is top-level retryable",
			stepErr:    runruntime.ErrCallbackNotReady,
			wantStatus: http.StatusConflict,
			wantCode:   "step_not_ready",
			wantStage:  "run", wantRetryable: true,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			router := workflowPublicTestRouter(t, publicRouteRunService{}, realHandlerStepRuntime{err: test.stepErr})
			response := postWorkflowStepCommand(t, router, map[string]any{
				"action": "complete", "workflowRunId": "wri_1", "workflowStepId": "wfx_baa86a9e8b16589a778ee2cfce0bef62",
				"idempotencyKey": "k-1", "output": map[string]any{},
			})
			require.Equal(t, test.wantStatus, response.Code, response.Body.String())
			var envelope struct {
				Code        string            `json:"code"`
				Stage       string            `json:"stage"`
				Path        string            `json:"path"`
				Retryable   bool              `json:"retryable"`
				Diagnostics []json.RawMessage `json:"diagnostics"`
			}
			require.NoError(t, json.Unmarshal(response.Body.Bytes(), &envelope))
			require.Equal(t, test.wantCode, envelope.Code)
			require.Equal(t, test.wantStage, envelope.Stage)
			require.Equal(t, test.wantPath, envelope.Path)
			require.Equal(t, test.wantRetryable, envelope.Retryable, "top-level retryable must match the real writer")
			require.Equal(t, test.wantDiags, len(envelope.Diagnostics))
		})
	}
}

// TestWorkflowStepRealHandlerWritesTruncationMetadata proves the real
// gin handler + ErrorWithContract writer emit the deterministic truncation
// metadata when the server's diagnostic budget cuts the typed diagnostics, and
// that within-budget diagnostics carry no truncation metadata.
func TestWorkflowStepRealHandlerWritesTruncationMetadata(t *testing.T) {
	gin.SetMode(gin.TestMode)
	index := 0
	diagnostics := make([]publicerr.Diagnostic, 0, publicerr.MaxServerDiagnostics+2)
	for i := 0; i < publicerr.MaxServerDiagnostics+2; i++ {
		diagnostics = append(diagnostics, publicerr.Diagnostic{
			Severity: publicerr.SeverityError,
			Code:     "callback.work_items.key_invalid",
			Path:     "callback.workItems[0].key",
			Field:    "key",
			Index:    &index,
			Message:  "key must match ^[a-z][a-z0-9_-]{0,255}$",
		})
	}
	validationErr := callbackValidationFailure(
		runruntime.CallbackValidationCodeWorkItems,
		diagnostics,
		runkernel.NewTestValidationErrorForTest(
			runkernel.TestValidationCodeWorkItemsKeyInvalid,
			"callback.workItems[0].key",
			"key",
			0,
			"key must match ^[a-z][a-z0-9_-]{0,255}$",
		),
	)
	router := workflowPublicTestRouter(t, publicRouteRunService{}, realHandlerStepRuntime{err: validationErr})
	response := postWorkflowStepCommand(t, router, map[string]any{
		"action": "complete", "workflowRunId": "wri_1", "workflowStepId": "wfx_baa86a9e8b16589a778ee2cfce0bef62",
		"idempotencyKey": "k-1", "output": map[string]any{},
	})
	require.Equal(t, http.StatusBadRequest, response.Code, response.Body.String())
	var envelope struct {
		Code        string `json:"code"`
		Diagnostics []any  `json:"diagnostics"`
		Truncation  struct {
			DiagnosticsTruncated bool     `json:"diagnosticsTruncated"`
			OmittedCount         int      `json:"omittedCount"`
			TruncatedFields      []string `json:"truncatedFields"`
		} `json:"truncation"`
	}
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &envelope))
	require.Equal(t, runruntime.CallbackValidationCodeWorkItems, envelope.Code)
	require.Len(t, envelope.Diagnostics, publicerr.MaxServerDiagnostics)
	require.True(t, envelope.Truncation.DiagnosticsTruncated)
	require.Equal(t, 2, envelope.Truncation.OmittedCount)

	// Within-budget diagnostics carry no truncation metadata at all.
	smallErr := callbackValidationFailure(
		runruntime.CallbackValidationCodeWorkItems,
		[]publicerr.Diagnostic{{
			Severity: publicerr.SeverityError,
			Code:     "callback.work_items.key_invalid",
			Path:     "callback.workItems[0].key",
			Field:    "key",
			Index:    &index,
			Message:  "key must match ^[a-z][a-z0-9_-]{0,255}$",
		}},
		runkernel.NewTestValidationErrorForTest(
			runkernel.TestValidationCodeWorkItemsKeyInvalid,
			"callback.workItems[0].key",
			"key",
			0,
			"key must match ^[a-z][a-z0-9_-]{0,255}$",
		),
	)
	router = workflowPublicTestRouter(t, publicRouteRunService{}, realHandlerStepRuntime{err: smallErr})
	response = postWorkflowStepCommand(t, router, map[string]any{
		"action": "complete", "workflowRunId": "wri_1", "workflowStepId": "wfx_baa86a9e8b16589a778ee2cfce0bef62",
		"idempotencyKey": "k-1", "output": map[string]any{},
	})
	require.Equal(t, http.StatusBadRequest, response.Code, response.Body.String())
	var small struct {
		Truncation json.RawMessage `json:"truncation"`
	}
	require.NoError(t, json.Unmarshal(response.Body.Bytes(), &small))
	require.Empty(t, small.Truncation, "within-budget diagnostics must not emit truncation metadata")
}
