// Package execution defines provider-only execution contracts shared by Node
// and Edge-facing provider transports.
package execution

import (
	"context"
	"errors"
	"time"
)

// DefaultSessionID is the correlation key used when no session_id is provided.
// Providers must not use it to identify, resume, or retain a process.
const DefaultSessionID = "default"

// ErrRunCancelled is returned by providers when a single run is cancelled.
var ErrRunCancelled = errors.New("run cancelled")

type ExecutionSpec struct {
	RunID                  string
	Adapter                string
	Target                 string
	SessionID              string
	Background             bool
	Policy                 map[string]any
	Input                  map[string]any
	TimeoutSec             int
	Metadata               map[string]string
	ResponseStallTimeoutMS int64
}

type EventType string

const (
	EventTypeStart          EventType = "start"
	EventTypeDelta          EventType = "delta"
	EventTypeReasoningDelta EventType = "reasoning_delta"
	EventTypeComplete       EventType = "complete"
	EventTypeError          EventType = "error"
	EventTypeCancelled      EventType = "cancelled"
)

type RuntimeEvent struct {
	RunID     string
	Type      EventType
	Delta     string
	Message   string
	Error     string
	Failure   *Failure
	Usage     *UsageStats
	Metadata  map[string]string
	Timestamp time.Time
}

// UsageStats reports token consumption for a completed run. ReasoningTokens and
// CachedInputTokens are populated only when the provider reports them; a zero
// value means "not reported" and downstream metric emit never estimates
// reasoning tokens from observed reasoning text.
type UsageStats struct {
	InputTokens       int
	OutputTokens      int
	ReasoningTokens   int
	CachedInputTokens int
}

// ProviderStatus is the minimal Edge-visible provider availability state.
type ProviderStatus string

const (
	ProviderStatusUnknown     ProviderStatus = "unknown"
	ProviderStatusAvailable   ProviderStatus = "available"
	ProviderStatusUnavailable ProviderStatus = "unavailable"
)

// NormalizeProviderStatus keeps provider state within the public baseline.
func NormalizeProviderStatus(status ProviderStatus) ProviderStatus {
	switch status {
	case ProviderStatusAvailable, ProviderStatusUnavailable:
		return status
	default:
		return ProviderStatusUnknown
	}
}

// Capabilities describes what a Provider can do.
type Capabilities struct {
	AdapterName      string
	InstanceKey      string // stable registry instance key; empty for single-instance adapters
	Targets          []string
	MaxConcurrency   int
	MaxQueue         int
	QueueTimeoutMS   int
	RequestTimeoutMS int
	ProviderStatus   ProviderStatus
}

// RunRequest is the host-neutral representation of an incoming run request.
type RunRequest struct {
	RunID                  string
	Adapter                string
	Target                 string
	SessionID              string
	Background             bool
	Policy                 map[string]any
	Input                  map[string]any
	TimeoutSec             int
	Metadata               map[string]string
	ResponseStallTimeoutMS int64
}

type CommandType string

const (
	CommandTypeCapabilities    CommandType = "capabilities"
	CommandTypeTransportStatus CommandType = "transport_status"
	CommandTypeOllamaAPI       CommandType = "ollama_api"
)

type CommandRequest struct {
	RequestID  string
	Type       CommandType
	Adapter    string
	Target     string
	SessionID  string
	TimeoutSec int
	Metadata   map[string]string
}

type CommandResponse struct {
	RequestID string
	Type      CommandType
	Adapter   string
	Target    string
	SessionID string
	Result    map[string]string
}

type CommandHandler interface {
	HandleCommand(ctx context.Context, req CommandRequest) (CommandResponse, error)
}

// ProviderProbeResult contains the target-aware probe result of a provider.
type ProviderProbeResult struct {
	AdapterName string
	InstanceKey string
	Target      string
	Targets     []string
	Status      ProviderStatus
	Detail      string
	Metadata    map[string]string
}

// ProviderProber is an optional interface that adapters may implement
// to support active endpoint and target availability checks.
type ProviderProber interface {
	ProbeProvider(ctx context.Context, target string) (ProviderProbeResult, error)
}

// EventSink receives RuntimeEvents emitted during execution.
type EventSink interface {
	Emit(ctx context.Context, event RuntimeEvent) error
}

// Provider executes an adapter target and streams events to a sink.
type Provider interface {
	Name() string
	Capabilities(ctx context.Context) (Capabilities, error)
	Execute(ctx context.Context, spec ExecutionSpec, sink EventSink) error
}

// Router resolves a RunRequest into a concrete ExecutionSpec and the Provider to execute it.
type Router interface {
	Resolve(ctx context.Context, req RunRequest) (ExecutionSpec, error)
	ResolveAdapter(ctx context.Context, req RunRequest) (ExecutionSpec, Provider, error)
	// LookupAdapter returns an adapter by instance key or legacy type-name, preserving
	// ambiguous-lookup errors so callers can surface the exact failure reason.
	LookupAdapter(adapterName string) (Provider, error)
	GetAdapter(adapterName string) (Provider, bool)
}

// PolicyEngine applies and validates policies on execution specs.
type PolicyEngine interface {
	Apply(ctx context.Context, spec ExecutionSpec) (ExecutionSpec, error)
	Validate(ctx context.Context, spec ExecutionSpec) error
}

// ProviderTunnelRequest represents a host request for raw provider tunnel execution.
type ProviderTunnelRequest struct {
	RunID    string
	TunnelID string
	Adapter  string
	Target   string
	Method   string
	Path     string
	// Operation is the protocol operation id (e.g. "chat_completions",
	// "messages", "models"). When set, the Node adapter resolves the request
	// URL from the concrete profile's operation path. When empty, the legacy
	// Path field is used as a mixed-version fallback.
	Operation              string
	Headers                map[string]string
	Body                   []byte
	Stream                 bool
	TimeoutSec             int
	Metadata               map[string]string
	SessionID              string
	Credential             *ProviderCredential
	ResponseStallTimeoutMS int64
}

// ProviderCredential is request-local plaintext owned by the Node adapter.
// It is never serialized, logged, or placed in generic headers before the
// final upstream request boundary.
type ProviderCredential struct {
	HeaderName string
	Scheme     string
	Secret     []byte
}

func (c *ProviderCredential) Zero() {
	if c == nil {
		return
	}
	for i := range c.Secret {
		c.Secret[i] = 0
	}
	c.Secret = nil
}

// ProviderTunnelFrameKind classifies a ProviderTunnelFrame.
type ProviderTunnelFrameKind string

const (
	ProviderTunnelFrameKindResponseStart ProviderTunnelFrameKind = "response_start"
	ProviderTunnelFrameKindBody          ProviderTunnelFrameKind = "body"
	ProviderTunnelFrameKindEnd           ProviderTunnelFrameKind = "end"
	ProviderTunnelFrameKindError         ProviderTunnelFrameKind = "error"
	ProviderTunnelFrameKindUsage         ProviderTunnelFrameKind = "usage"
)

// ProviderTunnelFrame carries raw provider response segments to Edge.
type ProviderTunnelFrame struct {
	RunID      string
	TunnelID   string
	Sequence   int64
	Kind       ProviderTunnelFrameKind
	StatusCode int
	Headers    map[string]string
	Body       []byte
	End        bool
	Error      string
	// Failure is optional for backward compatibility; transport mappers own
	// its wire serialization.
	Failure   *Failure
	Usage     *UsageStats
	Metadata  map[string]string
	Timestamp time.Time
}

// ProviderTunnelSink receives ProviderTunnelFrames emitted during provider tunnel execution.
type ProviderTunnelSink interface {
	EmitTunnelFrame(ctx context.Context, frame ProviderTunnelFrame) error
}

// ProviderTunnelAdapter is implemented by adapters that support direct HTTP/SSE raw tunneling.
type ProviderTunnelAdapter interface {
	Provider
	TunnelProvider(ctx context.Context, req ProviderTunnelRequest, sink ProviderTunnelSink) error
}
