// Package runtime is the provider-agnostic adapter for LLM agents. The
// Runtime interface hides whether an agent is HeadlessClaude (HTTP SDK),
// HeadlessOpenAI (later), or TmuxPaneCLI (tmux + sidecar parsing).
//
// Plan §13. The interface stays narrow on purpose: spawn, send, receive,
// terminate, health, snapshot. Everything else (tool execution, accounting,
// zone checks) lives in the agent loop, which uses the runtime through this
// interface.
package runtime

import (
	"context"
	"errors"

	"github.com/flothus/tmux-xterm-research/server-go/internal/harness/envelope"
)

// SpawnSpec describes a new agent to spawn.
type SpawnSpec struct {
	AgentID  string
	Role     string
	Provider string
	RunID    string
	// Tools is the allowlist of tool names the agent can invoke.
	Tools []string
	// ZoneScope is the path-glob list defining the agent's effective write scope.
	ZoneScope []string
	// Prompt is the assembled initial prompt (role + introspect bundle + task).
	Prompt string
}

// LLMRequest captures a single turn's input. The Runtime should call its
// configured LLM, return the response, and record tokens via the wrapper.
type LLMRequest struct {
	Prompt string
	// IncomingMessage is the envelope-formatted inbox message the agent should
	// respond to. May be nil for a synthetic spawn turn.
	IncomingMessage *envelope.Envelope
	// AvailableTools is the per-turn list of tool names the agent may invoke.
	AvailableTools []string
	// PriorParseError (L8): set when the agent's most recent turn ended
	// in a parse failure. The Runtime should prepend a short corrective
	// note to the prompt so the model knows why its prior output was
	// rejected. Without this the model just keeps emitting the same
	// malformed output until the dead-letter sweep fires.
	PriorParseError string
}

// LLMResponse is the structured output of one turn. ToolCalls are extracted
// from the LLM's structured output (or parsed from a sidecar block).
type LLMResponse struct {
	// Text is the model's prose output. Free text and rhetorical questions
	// live here; they're not auto-routed as clarifications.
	Text string
	// ToolCalls is the list of structured tool invocations.
	ToolCalls []ToolCall
	// Tokens is the (prompt, completion, total) accounting for cost tracking.
	Tokens TokenUsage
	// ParseFailure is set if the runtime had to retry parsing the response
	// because the model produced malformed structured output. Used to drive
	// the parse_failure_rate metric.
	ParseFailures int
}

// TokenUsage is a minimal token accounting struct.
type TokenUsage struct {
	Prompt     int
	Completion int
}

// ToolCall is a single requested tool invocation.
type ToolCall struct {
	Name string
	// Args is JSON-able input.
	Args map[string]any
	// ID is the model-supplied call id (for matching with results).
	ID string
}

// Runtime is the provider-agnostic adapter.
type Runtime interface {
	// Spawn binds a new agent to this runtime and returns its id (matches
	// SpawnSpec.AgentID if provided).
	Spawn(ctx context.Context, spec SpawnSpec) (string, error)
	// CallLLM runs one turn for an agent. Implementations are responsible for
	// retrying parse failures up to the runtime's per-instance limit and for
	// returning ParseFailures>=1 in the response when retries were needed.
	CallLLM(ctx context.Context, agentID string, req LLMRequest) (*LLMResponse, error)
	// Terminate ends the agent.
	Terminate(ctx context.Context, agentID, reason string) error
	// Health returns the current health hint (the orchestrator's deadline is
	// the real liveness signal; this is for runtime-level crash detection only).
	Health(ctx context.Context, agentID string) (string, error)
}

// ErrUnknownAgent is returned by runtimes when asked about an agent they
// don't manage.
var ErrUnknownAgent = errors.New("runtime: unknown agent")
