package openai

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/http"
	"sort"
	"strings"
	"sync"

	edgeservice "iop/apps/edge/internal/service"
	"iop/packages/go/singlerequesttemplate"
)

const (
	singleRequestWorkPrompt                       = "Read the supplied plan, use only the supplied workspace tools when needed, then return exactly one JSON object with non-empty string fields item_status, changes, verification, and deviations. item_status must list every plan step id P1..Pn exactly once, one bullet per line, each in the form \"- P1: completed\". deviations must be a non-empty string, conventionally \"None\" when there are no deviations. Every relative_path argument and every workspace path mentioned in item_status, changes, verification, or deviations must be canonical and workspace-relative: use README.md, never ./README.md, an absolute path, or a parent traversal."
	singleRequestWorkStageID                      = "work"
	singleRequestCanonicalRelativePathDescription = "Canonical path relative to the workspace root. Never start with /, ./, or ../; use README.md rather than ./README.md."
)

var errSingleRequestWorkStage = errors.New("single-request work stage: failed")

type singleRequestWorkToolKey struct {
	requestID  string
	stageID    string
	toolCallID string
}

// singleRequestWorkToolBridge is the request-local continuation boundary for
// private Plan, Work, and Review provider calls. It retains only correlation
// identifiers and one bounded result slot; prompts, arguments, paths, and
// provider payloads never enter the map.
type singleRequestWorkToolBridge struct {
	mu      sync.Mutex
	pending map[singleRequestWorkToolKey]chan edgeservice.InternalWorkspaceToolResult
}

func newSingleRequestWorkToolBridge() *singleRequestWorkToolBridge {
	return &singleRequestWorkToolBridge{pending: make(map[singleRequestWorkToolKey]chan edgeservice.InternalWorkspaceToolResult)}
}

func (b *singleRequestWorkToolBridge) register(key singleRequestWorkToolKey) (<-chan edgeservice.InternalWorkspaceToolResult, error) {
	if b == nil || key.requestID == "" || key.stageID == "" || key.toolCallID == "" {
		return nil, errSingleRequestWorkStage
	}
	b.mu.Lock()
	defer b.mu.Unlock()
	if _, exists := b.pending[key]; exists {
		return nil, errSingleRequestWorkStage
	}
	ch := make(chan edgeservice.InternalWorkspaceToolResult, 1)
	b.pending[key] = ch
	return ch, nil
}

func (b *singleRequestWorkToolBridge) unregister(key singleRequestWorkToolKey) {
	if b == nil {
		return
	}
	b.mu.Lock()
	delete(b.pending, key)
	b.mu.Unlock()
}

func (b *singleRequestWorkToolBridge) wait(ctx context.Context, key singleRequestWorkToolKey, ch <-chan edgeservice.InternalWorkspaceToolResult) (edgeservice.InternalWorkspaceToolResult, error) {
	defer b.unregister(key)
	select {
	case <-ctx.Done():
		return edgeservice.InternalWorkspaceToolResult{}, errSingleRequestWorkStage
	case result := <-ch:
		return result.Clone(), nil
	}
}

// ContinueInternalTool delivers a result after releasing the correlation map
// lock. A stale, duplicate, malformed, or already-cancelled continuation is
// rejected without retaining a waiter.
func (b *singleRequestWorkToolBridge) ContinueInternalTool(_ context.Context, result edgeservice.InternalWorkspaceToolResult) error {
	key := singleRequestWorkToolKey{requestID: result.RequestID, stageID: result.StageID, toolCallID: result.ToolCallID}
	if b == nil || key.requestID == "" || key.stageID == "" || key.toolCallID == "" {
		return errSingleRequestWorkStage
	}
	b.mu.Lock()
	ch, ok := b.pending[key]
	if ok {
		delete(b.pending, key)
	}
	b.mu.Unlock()
	if !ok {
		return errSingleRequestWorkStage
	}
	ch <- result.Clone()
	return nil
}

func (b *singleRequestWorkToolBridge) pendingCount() int {
	if b == nil {
		return 0
	}
	b.mu.Lock()
	defer b.mu.Unlock()
	return len(b.pending)
}

func (b *singleRequestWorkToolBridge) clearRequest(requestID string) {
	if b == nil || requestID == "" {
		return
	}
	b.mu.Lock()
	defer b.mu.Unlock()
	for key := range b.pending {
		if key.requestID == requestID {
			delete(b.pending, key)
		}
	}
}

type singleRequestWorkStage struct {
	provider *singleRequestProviderStage
	bridge   *singleRequestWorkToolBridge
}

func newSingleRequestWorkStage(provider *singleRequestProviderStage, bridge *singleRequestWorkToolBridge) *singleRequestWorkStage {
	return &singleRequestWorkStage{provider: provider, bridge: bridge}
}

type singleRequestWorkStageRequest struct {
	RequestID        string
	Task             string
	StageBinding     edgeservice.SingleRequestStageBinding
	Limits           edgeservice.SingleRequestLimits
	NodeRef          string
	SessionID        string
	UsageAttribution string
	Sequence         uint64
	Quality          *singleRequestQualityGate
}

type singleRequestWorkCompletion struct {
	ItemStatus   string `json:"item_status"`
	Changes      string `json:"changes"`
	Verification string `json:"verification"`
	Deviations   string `json:"deviations"`
}

func singleRequestWorkResponseFormat() *singleRequestProviderResponseFormat {
	return &singleRequestProviderResponseFormat{
		Type: "json_schema",
		JSONSchema: singleRequestProviderResponseJSONSchema{
			Name:   "single_request_work",
			Strict: true,
			Schema: singleRequestProviderOutputSchema{
				Type: "object",
				Properties: map[string]singleRequestProviderOutputProperty{
					"item_status": {
						Type:        "string",
						Description: "One bullet per plan step id (P1..Pn) exactly once, each as \"- P1: completed\".",
						MinLength:   1,
					},
					"changes": {
						Type:        "string",
						Description: "A concise description of the completed workspace work.",
						MinLength:   1,
					},
					"verification": {
						Type:        "string",
						Description: "A concise description of the completed verification.",
						MinLength:   1,
					},
					"deviations": {
						Type:        "string",
						Description: "A concise description of plan deviations, or \"None\".",
						MinLength:   1,
					},
				},
				Required:             []string{"item_status", "changes", "verification", "deviations"},
				AdditionalProperties: false,
			},
		},
	}
}

func (v *singleRequestWorkCompletion) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "item_status", "changes", "verification", "deviations"); err != nil {
		return err
	}
	type alias singleRequestWorkCompletion
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkCompletion(decoded)
	return nil
}

func (s *singleRequestWorkStage) run(ctx context.Context, req singleRequestWorkStageRequest, ctrl edgeservice.SingleRequestController) error {
	quality := singleRequestQualityGateOrNew(req.Quality)
	if s == nil || s.provider == nil || s.provider.service == nil || s.bridge == nil || ctrl == nil || req.RequestID == "" || req.Task == "" || req.Sequence == 0 || req.StageBinding.Dispatch == nil {
		return quality.validation(errSingleRequestWorkStage)
	}
	if _, forbidden := req.StageBinding.Options["reasoning_effort"]; forbidden {
		return quality.validation(errSingleRequestWorkStage)
	}
	binding := ctrl.Binding()
	if binding == nil || binding.Workspace == nil || binding.Workspace.NodeID == "" || binding.Templates.Review == "" || req.NodeRef != binding.Workspace.NodeID {
		return quality.validation(errSingleRequestWorkStage)
	}
	plan, err := ctrl.ReadInternalArtifact(ctx, edgeservice.SingleRequestArtifactPlan)
	if err != nil {
		return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
	}
	if len(plan) == 0 {
		return quality.malformed(errSingleRequestWorkStage)
	}
	if len(plan) > req.Limits.MaxOutputBytes {
		return quality.length(errSingleRequestWorkStage)
	}
	// Enforce the frozen effective Plan template before extracting IDs. A
	// stored PLAN that fails the exact parser cannot reach provider dispatch
	// or the REVIEW handoff write, closing the grammar bypass.
	if _, err := singlerequesttemplate.ParsePlan(binding.Templates.Plan, string(plan), req.Limits.MaxOutputBytes); err != nil {
		return quality.malformed(errSingleRequestWorkStage)
	}
	planIDs, err := singlerequesttemplate.PlanItemIDs(plan)
	if err != nil {
		return quality.malformed(errSingleRequestWorkStage)
	}
	tools, err := singleRequestWorkTools(binding.Workspace)
	if err != nil {
		return quality.validation(errSingleRequestWorkStage)
	}
	sequence := req.Sequence
	if err := ctrl.SubmitEnvelope(edgeservice.SingleRequestEnvelope{RequestID: req.RequestID, Sequence: sequence, Stage: edgeservice.SingleRequestStateWorking}); err != nil {
		return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
	}
	messages := []chatMessage{
		{Role: "system", Content: singleRequestWorkPrompt},
		{Role: "user", Content: "Task:\n" + strings.TrimSpace(req.Task) + "\n\nPLAN:\n" + string(plan)},
	}
	completionEligible := false
	for {
		response, err := s.submit(ctx, req, messages, tools, completionEligible)
		if err != nil {
			return quality.reclassify(err, errSingleRequestWorkStage)
		}
		if response.completion != nil {
			if !completionEligible {
				return quality.malformed(errSingleRequestWorkStage)
			}
			return s.finalizeReviewHandoff(ctx, req, ctrl, binding.Templates.Review, response.completion, planIDs, quality)
		}
		call := response.call
		if call == nil {
			return quality.malformed(errSingleRequestWorkStage)
		}
		arguments, err := decodeSingleRequestWorkToolArguments(call.Function.Arguments)
		if err != nil {
			return quality.malformed(errSingleRequestWorkStage)
		}
		arguments = normalizeSingleRequestProviderToolArguments(call.Function.Name, arguments)
		key := singleRequestWorkToolKey{requestID: req.RequestID, stageID: singleRequestWorkStageID, toolCallID: call.ID}
		resultCh, err := s.bridge.register(key)
		if err != nil {
			return quality.internalTool(errSingleRequestWorkStage)
		}
		sequence++
		toolCall := &edgeservice.InternalWorkspaceToolCall{RequestID: req.RequestID, StageID: key.stageID, ToolCallID: call.ID, Name: call.Function.Name, Arguments: arguments}
		if err := ctrl.SubmitEnvelope(edgeservice.SingleRequestEnvelope{RequestID: req.RequestID, Sequence: sequence, Stage: edgeservice.SingleRequestStateInternalTool, SavedStage: edgeservice.SingleRequestStateWorking, ToolCall: toolCall}); err != nil {
			s.bridge.unregister(key)
			return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
		}
		result, err := s.bridge.wait(ctx, key, resultCh)
		if err != nil {
			return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
		}
		if err := quality.observeToolCycle(singleRequestWorkStageID, call.Function.Name, arguments, result, errSingleRequestWorkStage); err != nil {
			return err
		}
		completionEligible = result.Status == "success" && result.ErrorCode == ""
		messages = append(messages,
			chatMessage{Role: "assistant", ToolCalls: []any{call.asChatToolCall()}},
			chatMessage{Role: "tool", ToolCallID: call.ID, ToolName: call.Function.Name, Content: singleRequestWorkToolResultContent(result, req.Limits.MaxOutputBytes)},
		)
		sequence++
		if err := ctrl.SubmitEnvelope(edgeservice.SingleRequestEnvelope{RequestID: req.RequestID, Sequence: sequence, Stage: edgeservice.SingleRequestStateWorking, SavedStage: edgeservice.SingleRequestStateWorking}); err != nil {
			return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
		}
	}
}

func singleRequestWorkTools(workspace *edgeservice.SingleRequestWorkspaceBinding) ([]any, error) {
	if workspace == nil || len(workspace.OperationIDs) == 0 {
		return nil, errSingleRequestWorkStage
	}
	has := func(id string) bool {
		for _, candidate := range workspace.OperationIDs {
			if candidate == id {
				return true
			}
		}
		return false
	}
	tools := make([]any, 0, len(workspace.OperationIDs))
	pathProperty := map[string]any{"type": "string", "minLength": 1, "description": singleRequestCanonicalRelativePathDescription}
	path := map[string]any{"type": "object", "additionalProperties": false, "required": []string{"relative_path"}, "properties": map[string]any{"relative_path": pathProperty}}
	for _, pair := range []struct{ operation, name string }{{"read", edgeservice.InternalWorkspaceToolRead}, {"list", edgeservice.InternalWorkspaceToolList}, {"write", edgeservice.InternalWorkspaceToolWrite}, {"delete", edgeservice.InternalWorkspaceToolDelete}} {
		if !has(pair.operation) {
			continue
		}
		parameters := path
		if pair.operation == "write" {
			parameters = map[string]any{"type": "object", "additionalProperties": false, "required": []string{"relative_path", "content"}, "properties": map[string]any{"relative_path": pathProperty, "content": map[string]any{"type": "string"}}}
		}
		tools = append(tools, singleRequestWorkToolSchema(pair.name, parameters))
	}
	if has("command") {
		if len(workspace.CommandIDs) == 0 {
			return nil, errSingleRequestWorkStage
		}
		environmentNames := append([]string(nil), workspace.EnvironmentNames...)
		sort.Strings(environmentNames)
		environmentProperties := make(map[string]any, len(environmentNames))
		for _, name := range environmentNames {
			environmentProperties[name] = map[string]any{"type": "string"}
		}
		tools = append(tools, singleRequestWorkToolSchema(edgeservice.InternalWorkspaceToolCommand, map[string]any{
			"type": "object", "additionalProperties": false, "required": []string{"command_id"},
			"properties": map[string]any{"command_id": map[string]any{"type": "string", "enum": append([]string(nil), workspace.CommandIDs...)}, "environment": map[string]any{"type": "object", "additionalProperties": false, "properties": environmentProperties}},
		}))
	}
	if len(tools) == 0 {
		return nil, errSingleRequestWorkStage
	}
	return tools, nil
}

func singleRequestWorkToolSchema(name string, parameters map[string]any) map[string]any {
	return map[string]any{"type": "function", "function": map[string]any{"name": name, "description": "Approved IOP workspace operation.", "strict": true, "parameters": parameters}}
}

type singleRequestWorkProviderResponse struct {
	call       *singleRequestWorkProviderToolCall
	completion *singleRequestWorkCompletion
}

type singleRequestWorkProviderEnvelope struct {
	ID      string                            `json:"id"`
	Object  string                            `json:"object"`
	Created int64                             `json:"created"`
	Model   string                            `json:"model"`
	Choices []singleRequestWorkProviderChoice `json:"choices"`
	Usage   *singleRequestChatUsage           `json:"usage,omitempty"`
}

type singleRequestWorkProviderChoice struct {
	Index        int                              `json:"index"`
	FinishReason string                           `json:"finish_reason"`
	Message      singleRequestWorkProviderMessage `json:"message"`
}

type singleRequestWorkProviderMessage struct {
	Role             string                              `json:"role"`
	Content          *string                             `json:"content"`
	ToolCalls        []singleRequestWorkProviderToolCall `json:"tool_calls"`
	ReasoningContent *string                             `json:"reasoning_content,omitempty"`
}

type singleRequestWorkProviderToolCall struct {
	ID       string                            `json:"id"`
	Type     string                            `json:"type"`
	Function singleRequestWorkProviderFunction `json:"function"`
}

type singleRequestWorkProviderFunction struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"`
}

func (v *singleRequestWorkProviderEnvelope) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "id", "object", "created", "model", "choices", "usage", "system_fingerprint", "timings"); err != nil {
		return err
	}
	type alias singleRequestWorkProviderEnvelope
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkProviderEnvelope(decoded)
	return nil
}

func (v *singleRequestWorkProviderChoice) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "index", "finish_reason", "message"); err != nil {
		return err
	}
	type alias singleRequestWorkProviderChoice
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkProviderChoice(decoded)
	return nil
}

func (v *singleRequestWorkProviderMessage) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "role", "content", "tool_calls", "reasoning_content"); err != nil {
		return err
	}
	type alias singleRequestWorkProviderMessage
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkProviderMessage(decoded)
	return nil
}

func (v *singleRequestWorkProviderToolCall) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "id", "type", "function"); err != nil {
		return err
	}
	type alias singleRequestWorkProviderToolCall
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkProviderToolCall(decoded)
	return nil
}

func (v *singleRequestWorkProviderFunction) UnmarshalJSON(data []byte) error {
	if err := validateSingleRequestObjectFields(data, "name", "arguments"); err != nil {
		return err
	}
	type alias singleRequestWorkProviderFunction
	var decoded alias
	if err := json.Unmarshal(data, &decoded); err != nil {
		return err
	}
	*v = singleRequestWorkProviderFunction(decoded)
	return nil
}

func (c singleRequestWorkProviderToolCall) asChatToolCall() map[string]any {
	return map[string]any{"id": c.ID, "type": c.Type, "function": map[string]any{"name": c.Function.Name, "arguments": c.Function.Arguments}}
}

func (s *singleRequestWorkStage) submit(ctx context.Context, req singleRequestWorkStageRequest, messages []chatMessage, tools []any, completionEligible bool) (*singleRequestWorkProviderResponse, error) {
	quality := singleRequestQualityGateOrNew(req.Quality)
	dispatch := req.StageBinding.Dispatch
	stageCtx, cancel := providerStageContext(ctx, req.Limits.StageTimeoutMS)
	defer cancel()
	poolReq := edgeservice.ProviderPoolDispatchRequest{
		Run: edgeservice.SubmitRunRequest{NodeRef: req.NodeRef, ModelGroupKey: dispatch.ModelGroupKey, ProviderID: dispatch.ProviderID, UsageAttribution: req.UsageAttribution, SessionID: req.SessionID, TimeoutSec: dispatch.TimeoutSec, MaxQueue: dispatch.MaxQueue, QueueTimeoutMS: dispatch.QueueTimeoutMS, ProviderPool: true},
		Tunnel: edgeservice.SubmitProviderTunnelRequest{CredentialBinding: dispatch.CredentialBindingSnapshot(), NodeRef: req.NodeRef, ModelGroupKey: dispatch.ModelGroupKey, ProviderID: dispatch.ProviderID, UsageAttribution: req.UsageAttribution, Adapter: "openai_compat", Target: dispatch.UpstreamModel, SessionID: req.SessionID, Method: http.MethodPost, Path: "/v1/chat/completions", Stream: false, TimeoutSec: dispatch.TimeoutSec, MaxQueue: dispatch.MaxQueue, QueueTimeoutMS: dispatch.QueueTimeoutMS, ProviderPool: true, BuildBody: func(target string) ([]byte, error) {
			return buildSingleRequestWorkBody(messages, req.StageBinding.Options, tools, target, completionEligible)
		}},
	}
	requirements := singleRequestProviderRequirements(req.StageBinding.Options, tools, nil)
	if completionEligible {
		requirements.StructuredOutput = true
	}
	poolReq.AcceptCandidate = singleRequestProviderCandidatePredicate(dispatch, requirements)
	poolReq.PrepareProtocolTunnel = singleRequestProviderTunnelPreparer(requirements, func(target string) ([]byte, error) {
		return buildSingleRequestWorkBody(messages, req.StageBinding.Options, tools, target, completionEligible)
	})
	result, err := s.provider.service.SubmitProviderPool(stageCtx, poolReq)
	if err != nil || result == nil || result.Tunnel == nil {
		return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
	}
	if !providerStageDispatchMatches(result, dispatch) {
		return nil, quality.validation(errSingleRequestWorkStage)
	}
	defer result.Tunnel.Close()
	body, err := collectProviderStageFrames(stageCtx, result.Tunnel.Stream().Frames, req.Limits.MaxOutputBytes)
	if err != nil {
		return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
	}
	body, err = normalizeSingleRequestProviderResponse(body, result.DispatchInfo)
	if err != nil {
		return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
	}
	response, err := decodeSingleRequestWorkProviderResponse(body, req.Limits.MaxOutputBytes)
	if err != nil {
		if !errors.Is(err, errProviderStageOutputLimit) && !errors.Is(err, errProviderStageContextLimit) {
			return nil, quality.malformed(errSingleRequestWorkStage)
		}
		return nil, quality.providerFailure(stageCtx, err, errSingleRequestWorkStage)
	}
	return response, nil
}

func buildSingleRequestWorkBody(messages []chatMessage, options map[string]any, tools []any, target string, completionEligible bool) ([]byte, error) {
	if target == "" || len(messages) == 0 || len(tools) == 0 {
		return nil, errSingleRequestWorkStage
	}
	toolChoice := "required"
	if completionEligible {
		toolChoice = "auto"
	}
	body := map[string]any{"model": target, "messages": messages, "tools": tools, "tool_choice": toolChoice, "parallel_tool_calls": false, "stream": false}
	if completionEligible {
		body["response_format"] = singleRequestWorkResponseFormat()
	}
	for key, value := range options {
		folded := strings.ToLower(key)
		if isSingleRequestWorkReservedOption(folded) && key != folded {
			return nil, errSingleRequestWorkStage
		}
		if folded == "reasoning_effort" {
			return nil, errSingleRequestWorkStage
		}
		if isSingleRequestWorkReservedOption(folded) {
			continue
		}
		body[key] = value
	}
	return json.Marshal(body)
}

func isSingleRequestWorkReservedOption(key string) bool {
	switch key {
	case "model", "messages", "tools", "tool_choice", "parallel_tool_calls", "response_format", "stream", "credential", "credential_binding", "reasoning_effort":
		return true
	default:
		return false
	}
}

func decodeSingleRequestWorkToolArguments(arguments string) (json.RawMessage, error) {
	if arguments == "" {
		return nil, errSingleRequestWorkStage
	}
	raw := []byte(arguments)
	if validateSingleRequestJSON(raw) != nil {
		return nil, errSingleRequestWorkStage
	}
	decoder := json.NewDecoder(bytes.NewReader(raw))
	token, err := decoder.Token()
	delim, ok := token.(json.Delim)
	if err != nil || !ok || delim != '{' {
		return nil, errSingleRequestWorkStage
	}
	return append(json.RawMessage(nil), bytes.TrimSpace(raw)...), nil
}

// normalizeSingleRequestProviderToolArguments canonicalizes the one
// containment-safe provider convention that differs from the internal tool
// contract: an empty workspace_list path means the workspace root. All other
// operations and non-canonical paths remain unchanged and are rejected by the
// service-owned decoder.
func normalizeSingleRequestProviderToolArguments(name string, arguments json.RawMessage) json.RawMessage {
	if name != edgeservice.InternalWorkspaceToolList {
		return arguments
	}
	var pathArguments struct {
		RelativePath *string `json:"relative_path"`
	}
	decoder := json.NewDecoder(bytes.NewReader(arguments))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&pathArguments); err != nil || pathArguments.RelativePath == nil || *pathArguments.RelativePath != "" {
		return arguments
	}
	var trailing any
	if err := decoder.Decode(&trailing); err != io.EOF {
		return arguments
	}
	return json.RawMessage(`{"relative_path":"."}`)
}

func decodeSingleRequestWorkProviderResponse(body []byte, maximum int) (*singleRequestWorkProviderResponse, error) {
	if len(body) == 0 || len(body) > maximum || validateSingleRequestJSON(body) != nil {
		return nil, errSingleRequestWorkStage
	}
	var envelope singleRequestWorkProviderEnvelope
	decoder := json.NewDecoder(bytes.NewReader(body))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&envelope); err != nil || len(envelope.Choices) != 1 {
		return nil, errSingleRequestWorkStage
	}
	choice := envelope.Choices[0]
	if choice.Index != 0 || choice.Message.Role != "assistant" {
		return nil, errSingleRequestWorkStage
	}
	var extra any
	if decoder.Decode(&extra) != io.EOF {
		return nil, errSingleRequestWorkStage
	}
	if choice.FinishReason == "length" {
		return nil, errors.Join(errSingleRequestWorkStage, errProviderStageOutputLimit)
	}
	if choice.FinishReason == "context_length" || choice.FinishReason == "context_length_exceeded" {
		return nil, errors.Join(errSingleRequestWorkStage, errProviderStageContextLimit)
	}
	if choice.FinishReason == "tool_calls" && (choice.Message.Content == nil || *choice.Message.Content == "") && len(choice.Message.ToolCalls) == 1 {
		call := choice.Message.ToolCalls[0]
		if call.ID == "" || call.Type != "function" || call.Function.Name == "" || call.Function.Arguments == "" {
			return nil, errSingleRequestWorkStage
		}
		return &singleRequestWorkProviderResponse{call: &call}, nil
	}
	if choice.FinishReason == "stop" && choice.Message.Content != nil && len(choice.Message.ToolCalls) == 0 {
		completion, err := decodeSingleRequestWorkResult(*choice.Message.Content, maximum)
		if err != nil {
			return nil, errSingleRequestWorkStage
		}
		return &singleRequestWorkProviderResponse{completion: completion}, nil
	}
	return nil, errSingleRequestWorkStage
}

func decodeSingleRequestWorkResult(raw string, maximum int) (*singleRequestWorkCompletion, error) {
	if len(raw) == 0 || len(raw) > maximum || validateSingleRequestJSON([]byte(raw)) != nil {
		return nil, errSingleRequestWorkStage
	}
	var result singleRequestWorkCompletion
	decoder := json.NewDecoder(strings.NewReader(raw))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&result); err != nil || strings.TrimSpace(result.ItemStatus) == "" || strings.TrimSpace(result.Changes) == "" || strings.TrimSpace(result.Verification) == "" || strings.TrimSpace(result.Deviations) == "" {
		return nil, errSingleRequestWorkStage
	}
	var extra any
	if decoder.Decode(&extra) != io.EOF {
		return nil, errSingleRequestWorkStage
	}
	return &singleRequestWorkCompletion{
		ItemStatus:   strings.TrimSpace(result.ItemStatus),
		Changes:      strings.TrimSpace(result.Changes),
		Verification: strings.TrimSpace(result.Verification),
		Deviations:   strings.TrimSpace(result.Deviations),
	}, nil
}

// finalizeReviewHandoff makes the persisted worker report the only handoff to
// Review. Rendering and then parsing it closes both the configured template
// grammar and the rendered PLAN-to-item-status correspondence before a write.
func (s *singleRequestWorkStage) finalizeReviewHandoff(ctx context.Context, req singleRequestWorkStageRequest, ctrl edgeservice.SingleRequestController, tmpl string, completion *singleRequestWorkCompletion, planIDs []string, quality *singleRequestQualityGate) error {
	if completion == nil || len(planIDs) == 0 {
		return quality.malformed(errSingleRequestWorkStage)
	}
	handoff, err := singlerequesttemplate.RenderReview(tmpl, singlerequesttemplate.ReviewFields{
		ItemStatus:   completion.ItemStatus,
		Changes:      completion.Changes,
		Verification: completion.Verification,
		Deviations:   completion.Deviations,
	}, req.Limits.MaxOutputBytes)
	if err != nil || singlerequesttemplate.ValidateReviewHandoff(handoff, planIDs) != nil {
		return quality.malformed(errSingleRequestWorkStage)
	}
	if err := ctrl.WriteInternalArtifact(ctx, edgeservice.SingleRequestArtifactReview, handoff); err != nil {
		return quality.serviceFailure(ctx, err, errSingleRequestWorkStage)
	}
	return nil
}

func singleRequestWorkToolResultContent(result edgeservice.InternalWorkspaceToolResult, maximum int) string {
	if maximum < 1 {
		return ""
	}
	content, err := json.Marshal(map[string]any{"status": result.Status, "error_code": result.ErrorCode, "content": string(result.Content), "entries": result.Entries, "stdout": string(result.Stdout), "stderr": string(result.Stderr), "exit_code": result.ExitCode, "truncated": result.Truncated})
	if err != nil {
		if maximum >= 2 {
			return "{}"
		}
		return ""
	}
	if len(content) > maximum {
		const fallback = `{"truncated":true}`
		if len(fallback) <= maximum {
			return fallback
		}
		if maximum >= 2 {
			return "{}"
		}
		return ""
	}
	return string(content)
}
