package service

import (
	"sync"
	"time"

	iop "iop/proto/gen/iop"
)

// contextClassLong is the ContextClass value that marks a request for
// long-context admission (long slot gating and reservation).
const contextClassLong = "long"

type SubmitRunRequest struct {
	NodeRef          string
	RunID            string
	ModelGroupKey    string
	ProviderID       string
	UsageAttribution string
	Adapter          string
	Target           string
	SessionID        string
	Prompt           string
	Input            map[string]any
	Background       bool
	TimeoutSec       int
	MaxQueue         int
	QueueTimeoutMS   int
	Metadata         map[string]string
	// EstimatedInputTokens is a conservative token-count approximation of the
	// request input.  It is used by long-context admission policy to gate
	// routing and queueing decisions.
	EstimatedInputTokens int
	// ContextClass is a classification tag used by long-context admission
	// policy.  Allowed values are "normal" and "long".
	ContextClass string
	// ProviderPool signals that this request should be dispatched via the
	// provider-pool catalog keyed by ModelGroupKey. Adapter and Target are
	// resolved per-candidate by resolveProviderPoolCandidates; the winning
	// candidate's ServedTarget is written into Target before BuildRunRequest.
	ProviderPool           bool
	ResponseStallTimeoutMS int64
}

// RunDispatch describes a dispatched run in surface-neutral terms. It is the
// metadata any caller (console, HTTP, future RPC) needs after submission.
type RunDispatch struct {
	RunID                  string
	NodeID                 string
	NodeLabel              string
	ModelGroupKey          string
	Adapter                string
	Target                 string
	SessionID              string
	Background             bool
	TimeoutSec             int
	ResponseStallTimeoutMS int64
	EstimatedInputTokens   int
	ContextClass           string
	ProviderID             string
	UsageAttribution       string
	ProviderType           string // non-empty for provider-pool dispatches
	ExecutionPath          string // non-empty for provider-pool dispatches
	ProfileID              string
	ProfileDriver          string
	ProfileOperation       string
	ProfileToolCallWire    string
	ProfileCapabilities    []string
	CredentialSlotRef      string
	CredentialRevision     uint64
	QueueReason            string
}

// RunStream carries asynchronous events for a dispatched foreground run.
// Background runs leave both channels nil.
type RunStream struct {
	Events     <-chan *iop.RunEvent
	NodeEvents <-chan *iop.EdgeNodeEvent
}

// RunResult describes the interface for a submitted execution run, allowing
// surface-neutral consumption of its dispatch info and event stream.
type RunResult interface {
	Dispatch() RunDispatch
	Stream() RunStream
	Close()
	WaitTimeout() time.Duration
}

// RunHandle is the legacy combined surface kept for existing callers; it
// embeds the surface-neutral DTOs so HTTP/API code can consume RunDispatch
// or RunStream directly without depending on this struct.
type RunHandle struct {
	RunDispatch
	RunStream
	closeOnce sync.Once
	close     func()
}

func (h *RunHandle) Close() {
	if h != nil && h.close != nil {
		h.closeOnce.Do(h.close)
	}
}

func (h *RunHandle) WaitTimeout() time.Duration {
	if h == nil {
		return time.Duration(DefaultTimeoutSec+5) * time.Second
	}
	return time.Duration(normalizeTimeoutSec(h.TimeoutSec)+5) * time.Second
}

func (h *RunHandle) Dispatch() RunDispatch {
	if h == nil {
		return RunDispatch{}
	}
	dispatch := h.RunDispatch
	dispatch.ProfileCapabilities = append([]string(nil), h.ProfileCapabilities...)
	return dispatch
}

func (h *RunHandle) Stream() RunStream {
	if h == nil {
		return RunStream{}
	}
	return h.RunStream
}
