package config

import (
	"fmt"
	"strings"
)

// Default provider-pool queue policy values. These are the only canonical
// defaults and are referenced by config, load, refresh, and admission paths.
const (
	DefaultProviderPoolMaxQueue       = 16
	DefaultProviderPoolQueueTimeoutMS = 30000
)

// EdgeProviderPoolConf is the Edge root-level queue policy that the runtime
// treats as the canonical, shared owner for every provider candidate in the
// pool. Legacy per-provider max_queue/queue_timeout_ms fields remain in
// NodeProviderConf only as compatibility carry-over and never influence the
// effective policy after LoadEdge migration.
type EdgeProviderPoolConf struct {
	// MaxQueue is the maximum queue depth for the entire provider pool.
	// Zero on decode is normalized to DefaultProviderPoolMaxQueue.
	MaxQueue int `mapstructure:"max_queue" yaml:"max_queue"`
	// QueueTimeoutMS is the per-request queue timeout in milliseconds.
	// Explicit 0 preserves the legacy no-timeout behavior and is intentionally
	// distinct from the default 30000.
	QueueTimeoutMS int `mapstructure:"queue_timeout_ms" yaml:"queue_timeout_ms"`
}

type EdgeConfig struct {
	Edge            EdgeInfo                `mapstructure:"edge"          yaml:"edge"`
	Server          EdgeServerConf          `mapstructure:"server"        yaml:"server"`
	Bootstrap       EdgeBootstrapConf       `mapstructure:"bootstrap"     yaml:"bootstrap"`
	OpenAI          EdgeOpenAIConf          `mapstructure:"openai"        yaml:"openai"`
	A2A             EdgeA2AConf             `mapstructure:"a2a"           yaml:"a2a"`
	TLS             TLSConf                 `mapstructure:"tls"           yaml:"tls"`
	Logging         LoggingConf             `mapstructure:"logging"       yaml:"logging"`
	Metrics         MetricsConf             `mapstructure:"metrics"       yaml:"metrics"`
	Console         EdgeConsoleConf         `mapstructure:"console"       yaml:"console"`
	ControlPlane    EdgeControlPlaneConf    `mapstructure:"control_plane" yaml:"control_plane"`
	CredentialPlane EdgeCredentialPlaneConf `mapstructure:"credential_plane" yaml:"credential_plane,omitempty"`
	Refresh         EdgeRefreshConf         `mapstructure:"refresh"       yaml:"refresh,omitempty"`
	// LongContextThresholdTokens is the input-token estimate at or above which
	// a request is classified as long-context for admission policy.
	LongContextThresholdTokens int `mapstructure:"long_context_threshold_tokens" yaml:"long_context_threshold_tokens,omitempty"`
	// ProviderPool is the canonical, shared queue policy for every provider
	// candidate in the pool. See EdgeProviderPoolConf for semantic details.
	ProviderPool EdgeProviderPoolConf `mapstructure:"provider_pool" yaml:"provider_pool"`
	Nodes        []NodeDefinition     `mapstructure:"nodes" yaml:"nodes"`
	// Models is the top-level model catalog.  Each entry defines a canonical
	// routing key (ID) and its provider-pool mapping.  When set it takes
	// precedence over the legacy openai.model_routes for runtime dispatch.
	Models []ModelCatalogEntry `mapstructure:"models" yaml:"models,omitempty"`
	// ProtocolProfiles is the top-level reusable protocol profile catalog.
	// It combines immutable built-ins with operator-defined custom overlays.
	// Each entry is keyed by a stable profile id and may extend a built-in or
	// another custom profile via the Base field. Resolution happens once at
	// config load into immutable ConcreteProtocolProfile snapshots carried
	// onto each provider.
	ProtocolProfiles map[string]ProtocolProfileConf `mapstructure:"protocol_profiles" yaml:"protocol_profiles,omitempty"`
	// ExecutionPresets is the top-level execution preset catalog. Each preset
	// declares a frozen execution shape (selector, allowed modes, per-mode routes,
	// workspace tools) that the runtime can activate without further negotiation.
	// Only registered mode descriptors (direct, light) are accepted at load time;
	// unsupported modes fail closed before runtime dispatch.
	ExecutionPresets []ExecutionPreset `mapstructure:"execution_presets" yaml:"execution_presets,omitempty"`
}

// EdgeInfo carries this edge instance's stable identity for loading and logging.
// It is not used for Control Plane registration.
type EdgeInfo struct {
	ID   string `mapstructure:"id"   yaml:"id"`
	Name string `mapstructure:"name" yaml:"name"`
}

// EdgeServerConf configures the Edge HTTP listen surface.
type EdgeServerConf struct {
	Listen        string `mapstructure:"listen"         yaml:"listen"`
	AdvertiseHost string `mapstructure:"advertise_host" yaml:"advertise_host"`
}

// EdgeBootstrapConf configures the Node bootstrap artifact HTTP server.
type EdgeBootstrapConf struct {
	ArtifactBaseURL string `mapstructure:"artifact_base_url" yaml:"artifact_base_url"`
	Listen          string `mapstructure:"listen"            yaml:"listen"`
	ArtifactDir     string `mapstructure:"artifact_dir"      yaml:"artifact_dir"`
}

// EdgeControlPlaneConf holds the outbound connector settings for the
// Control Plane-Edge wire. When Enabled is false or WireAddr is empty the
// connector is a no-op and existing Edge behaviour is unaffected.
type EdgeControlPlaneConf struct {
	Enabled              bool    `mapstructure:"enabled"               yaml:"enabled"`
	WireAddr             string  `mapstructure:"wire_addr"             yaml:"wire_addr"`
	ReconnectIntervalSec int     `mapstructure:"reconnect_interval_sec" yaml:"reconnect_interval_sec"`
	TLS                  TLSConf `mapstructure:"tls"                   yaml:"tls,omitempty"`
}

// EdgeCredentialPlaneConf enables the complete managed credential runtime.
// It is deliberately separate from legacy provider_auth so both sources can
// never be active by implicit precedence.
type EdgeCredentialPlaneConf struct {
	Enabled         bool `mapstructure:"enabled"        yaml:"enabled"`
	LeaseTTLSeconds int  `mapstructure:"lease_ttl_seconds" yaml:"lease_ttl_seconds,omitempty"`
	LeaseCacheSize  int  `mapstructure:"lease_cache_size" yaml:"lease_cache_size,omitempty"`
}

// CredentialPlaneMode is the effective credential source selected by the
// single credential_plane.enabled switch. It is derived runtime state, not a
// second configuration mechanism.
type CredentialPlaneMode uint8

const (
	CredentialPlaneModeLegacy CredentialPlaneMode = iota
	CredentialPlaneModeManaged
)

// Mode returns the only credential source mode admitted by this config.
func (c EdgeCredentialPlaneConf) Mode() CredentialPlaneMode {
	if c.Enabled {
		return CredentialPlaneModeManaged
	}
	return CredentialPlaneModeLegacy
}

// EdgeRefreshConf configures the Edge-local refresh admin HTTP server.
// When Enabled is false (default) the admin server is not started and
// config refresh is not available without a restart.
type EdgeRefreshConf struct {
	Enabled bool   `mapstructure:"enabled" yaml:"enabled"`
	Listen  string `mapstructure:"listen"  yaml:"listen"`
}

// NodeDefinition is the edge-side record for a pre-registered node.
type NodeDefinition struct {
	ID        string             `mapstructure:"id"         yaml:"id"` // stable node identity; if empty, a UUID v4 is auto-assigned (dev fallback only)
	Alias     string             `mapstructure:"alias"      yaml:"alias"`
	Token     string             `mapstructure:"token"      yaml:"token"`
	Adapters  AdaptersConf       `mapstructure:"adapters"   yaml:"adapters"`
	Providers []NodeProviderConf `mapstructure:"providers" yaml:"providers,omitempty"`
	Runtime   RuntimeConf        `mapstructure:"runtime"    yaml:"runtime"`
	// Workspaces is the operator-owned bounded capability catalog for this
	// node. Each entry is keyed by a globally unique, trimmed ref and declares
	// the allowed operations, command templates, environment variables, and
	// byte/time limits. Platform is one of the supported Unix workspace hosts
	// and must match the selected Node host exactly. An empty slice is
	// backward-compatible and preserved on load.
	Workspaces []WorkspaceDefinition `mapstructure:"workspaces" yaml:"workspaces,omitempty"`
}

const (
	// WorkspacePlatformDarwin identifies the supported macOS workspace host.
	WorkspacePlatformDarwin = "darwin"
	// WorkspacePlatformLinux identifies the supported Linux workspace host.
	WorkspacePlatformLinux = "linux"
)

// IsSupportedWorkspacePlatform reports whether platform is in the closed set
// implemented by the request-scoped workspace runtime. Windows and unknown
// hosts intentionally fail closed.
func IsSupportedWorkspacePlatform(platform string) bool {
	switch platform {
	case WorkspacePlatformDarwin, WorkspacePlatformLinux:
		return true
	default:
		return false
	}
}

// WorkspaceOperation is a closed-set operator-owned capability identifier.
// These identifiers are the only operations permitted in workspace definitions.
type WorkspaceOperation string

const (
	// WorkspaceOpRead is the read-only operation. It is live-applyable only
	// when the workspace definition itself has not changed; workspace
	// definition changes require a restart.
	WorkspaceOpRead WorkspaceOperation = "read"
	// WorkspaceOpList enumerates entries under a root path.
	WorkspaceOpList WorkspaceOperation = "list"
	// WorkspaceOpWrite creates or overwrites content under a root path.
	WorkspaceOpWrite WorkspaceOperation = "write"
	// WorkspaceOpDelete removes entries under a root path.
	WorkspaceOpDelete WorkspaceOperation = "delete"
	// WorkspaceOpCommand executes a pre-approved command template by id.
	WorkspaceOpCommand WorkspaceOperation = "command"
)

// knownWorkspaceOperations is the closed set of permitted workspace operations.
var knownWorkspaceOperations = map[WorkspaceOperation]struct{}{
	WorkspaceOpRead:    {},
	WorkspaceOpList:    {},
	WorkspaceOpWrite:   {},
	WorkspaceOpDelete:  {},
	WorkspaceOpCommand: {},
}

// WorkspaceDefinition is the operator-owned bounded capability catalog for a
// supported Unix Node workspace. Platform is either "darwin" or "linux" and
// must match the selected Node host exactly. Root is an absolute, clean path
// other than "/". Operations declare the closed-set capabilities; commands
// declare the approved command templates; the environment allowlist declares
// which env vars may be inherited into workspace command invocations. Limits
// bound byte and time budgets. The catalog is compiled into
// NodeRecord.Workspaces at load time and carried immutably through the
// NodeStore; runtime mutation is restart-required.
type WorkspaceDefinition struct {
	// Ref is the globally unique, trimmed operator-assigned identifier for
	// this workspace. It is the only lookup key used by runtime admission.
	Ref string `mapstructure:"ref" yaml:"ref"`
	// Platform is in the closed "darwin" or "linux" set. The Node runtime
	// additionally requires an exact match with its host before opening Root.
	Platform string `mapstructure:"platform" yaml:"platform"`
	// Root is the absolute, clean (no trailing slash, no "/" alone) filesystem
	// root path this workspace is bounded to. It is not stat'd on Edge and is
	// never included in execution presets.
	Root string `mapstructure:"root" yaml:"root"`
	// Operations is the closed-set of allowed operations. Each entry must be
	// one of the known workspace operations; duplicates are rejected.
	Operations []WorkspaceOperation `mapstructure:"operations" yaml:"operations"`
	// Commands is the operator-approved command template catalog. Each entry
	// is keyed by a unique id; the caller selects only the id. Present only
	// when "command" is in Operations.
	Commands []WorkspaceCommandDefinition `mapstructure:"commands" yaml:"commands,omitempty"`
	// EnvironmentAllowlist is the set of portable environment variable names
	// that may be inherited into workspace command invocations. Names must
	// be unique and portable (alphanumeric + underscore, non-numeric start).
	EnvironmentAllowlist []string `mapstructure:"environment_allowlist" yaml:"environment_allowlist,omitempty"`
	// MaxReadBytes is the maximum bytes allowed for a single read operation.
	// Must be positive.
	MaxReadBytes int `mapstructure:"max_read_bytes" yaml:"max_read_bytes,omitempty"`
	// MaxWriteBytes is the maximum bytes allowed for a single write operation.
	// Must be positive.
	MaxWriteBytes int `mapstructure:"max_write_bytes" yaml:"max_write_bytes,omitempty"`
	// MaxOutputBytes is the maximum bytes allowed for a single command output.
	// Must be positive.
	MaxOutputBytes int `mapstructure:"max_output_bytes" yaml:"max_output_bytes,omitempty"`
	// MaxCommandTimeoutMS is the maximum command execution time in
	// milliseconds. Must be positive.
	MaxCommandTimeoutMS int `mapstructure:"max_command_timeout_ms" yaml:"max_command_timeout_ms,omitempty"`
}

// WorkspaceCommandDefinition is an operator-approved command template. The
// caller/model selects only the id; the executable and args are fixed and
// cannot be overridden at request time.
type WorkspaceCommandDefinition struct {
	// ID is the unique operator-assigned identifier for this command template.
	ID string `mapstructure:"id" yaml:"id"`
	// Executable is the absolute clean path to the command binary. Must not
	// be empty and must not contain "/" segments that escape the workspace root.
	Executable string `mapstructure:"executable" yaml:"executable"`
	// Args is the fixed argument list. The caller/model cannot modify it.
	Args []string `mapstructure:"args" yaml:"args,omitempty"`
}

// OpenAIRouteEntry maps an external model id to an internal adapter/target routing.
// Fields not set here fall back to the top-level EdgeOpenAIConf defaults.
type OpenAIRouteEntry struct {
	Model          string `mapstructure:"model"            yaml:"model"`
	NodeRef        string `mapstructure:"node"             yaml:"node,omitempty"`
	ProviderID     string `mapstructure:"provider_id"      yaml:"provider_id,omitempty"`
	Adapter        string `mapstructure:"adapter"          yaml:"adapter,omitempty"`
	Target         string `mapstructure:"target"           yaml:"target"`
	SessionID      string `mapstructure:"session_id"       yaml:"session_id,omitempty"`
	TimeoutSec     int    `mapstructure:"timeout_sec"      yaml:"timeout_sec,omitempty"`
	MaxQueue       int    `mapstructure:"max_queue"        yaml:"max_queue,omitempty"`
	QueueTimeoutMS int    `mapstructure:"queue_timeout_ms" yaml:"queue_timeout_ms,omitempty"`
}

// EdgeOpenAIConf configures the OpenAI-compatible input surface.
type EdgeOpenAIConf struct {
	Enabled            bool                       `mapstructure:"enabled"              yaml:"enabled"`
	Listen             string                     `mapstructure:"listen"               yaml:"listen"`
	BearerToken        string                     `mapstructure:"bearer_token"         yaml:"bearer_token"`
	PrincipalTokens    []OpenAIPrincipalTokenConf `mapstructure:"principal_tokens"     yaml:"principal_tokens,omitempty"`
	ProviderAuth       EdgeOpenAIProviderAuthConf `mapstructure:"provider_auth"        yaml:"provider_auth,omitempty"`
	NodeRef            string                     `mapstructure:"node"                 yaml:"node"`
	ProviderID         string                     `mapstructure:"provider_id"          yaml:"provider_id,omitempty"`
	Adapter            string                     `mapstructure:"adapter"              yaml:"adapter"`
	Target             string                     `mapstructure:"target"               yaml:"target"`
	Models             []string                   `mapstructure:"models"               yaml:"models"`
	ModelRoutes        []OpenAIRouteEntry         `mapstructure:"model_routes"         yaml:"model_routes,omitempty"`
	SessionID          string                     `mapstructure:"session_id"           yaml:"session_id"`
	TimeoutSec         int                        `mapstructure:"timeout_sec"          yaml:"timeout_sec"`
	StrictOutput       bool                       `mapstructure:"strict_output"        yaml:"strict_output"`
	StrictStreamBuffer bool                       `mapstructure:"strict_stream_buffer" yaml:"strict_stream_buffer"`
	StreamEvidenceGate StreamEvidenceGateConf     `mapstructure:"stream_evidence_gate" yaml:"stream_evidence_gate,omitempty"`
	TLS                TLSConf                    `mapstructure:"tls"                  yaml:"tls,omitempty"`
}

// StreamEvidenceGateConf configures request-local Recovery Coordinator and ingress snapshot limits.
type StreamEvidenceGateConf struct {
	// Enabled routes supported Chat Completions, normalized Responses, and
	// provider-tunnel passthrough paths through the packages/go/streamgate
	// request runtime instead of the legacy eager-write path. Default false
	// preserves existing behavior exactly.
	Enabled bool `mapstructure:"enabled"                     yaml:"enabled,omitempty"`
	// Environment is the request-stable deployment selector input. Empty
	// defaults to dev; only the deployment labels supported by the active
	// rollout policy are accepted.
	Environment              string `mapstructure:"environment"                 yaml:"environment,omitempty"`
	MaxRequestFaultRecovery  *int   `mapstructure:"max_request_fault_recovery"  yaml:"max_request_fault_recovery,omitempty"`
	MaxStrategyFaultRecovery *int   `mapstructure:"max_strategy_fault_recovery" yaml:"max_strategy_fault_recovery,omitempty"`
	MaxIngressSnapshotBytes  int    `mapstructure:"max_ingress_snapshot_bytes"  yaml:"max_ingress_snapshot_bytes,omitempty"`
	// Filters declares the semantic output-validation filters that the request
	// runtime registers on top of the always-applicable core mechanics. An empty
	// slice keeps the legacy production behavior (core mechanics + request-local
	// tool validation) exactly. Each entry is keyed by its filter kind, which is
	// unique within the slice.
	Filters []StreamGateFilterPolicyConf `mapstructure:"filters" yaml:"filters,omitempty"`
}

// Stream-gate output-filter kinds. These are the caller-neutral semantic
// filters that consume the Stream Evidence Gate Core mechanics.
const (
	// StreamGateFilterRepeatGuard is the rolling-window repeat guard.
	StreamGateFilterRepeatGuard = "repeat_guard"
	// StreamGateFilterSchemaGate is the metadata.scheme terminal gate.
	StreamGateFilterSchemaGate = "schema_gate"
	// StreamGateFilterProviderError is the provider-error lifecycle participant.
	StreamGateFilterProviderError = "provider_error"

	// Supported request-stable deployment selector values.
	StreamGateEnvironmentDev     = "dev"
	StreamGateEnvironmentDevCorp = "dev-corp"
)

// Stream-gate output-filter enforcement modes mirrored at the config boundary.
const (
	StreamGateFilterEnforcementBlocking    = "blocking"
	StreamGateFilterEnforcementObserveOnly = "observe_only"
)

// Stream-gate output-filter selector types mirrored at the config boundary.
const (
	StreamGateFilterSelectorEnvironment = "environment"
	StreamGateFilterSelectorModelGroup  = "model_group"
	StreamGateFilterSelectorModel       = "model"
	StreamGateFilterSelectorProvider    = "provider"
)

// Stream-gate output-filter default and absolute bounds.
const (
	DefaultStreamGateFilterHoldEvidenceRunes = 500
	MaxStreamGateFilterHoldEvidenceRunes     = 65536
	DefaultStreamGateFilterTimeoutMS         = 5000
	MaxStreamGateFilterTimeoutMS             = 60000
)

// knownStreamGateFilterKinds is the closed set of configurable filter kinds.
var knownStreamGateFilterKinds = map[string]struct{}{
	StreamGateFilterRepeatGuard:   {},
	StreamGateFilterSchemaGate:    {},
	StreamGateFilterProviderError: {},
}

// knownStreamGateFilterEnforcements is the closed set of enforcement modes.
var knownStreamGateFilterEnforcements = map[string]struct{}{
	StreamGateFilterEnforcementBlocking:    {},
	StreamGateFilterEnforcementObserveOnly: {},
}

// knownStreamGateFilterSelectorTypes is the closed set of selector types.
var knownStreamGateFilterSelectorTypes = map[string]struct{}{
	StreamGateFilterSelectorEnvironment: {},
	StreamGateFilterSelectorModelGroup:  {},
	StreamGateFilterSelectorModel:       {},
	StreamGateFilterSelectorProvider:    {},
}

// StreamGateFilterSelectorConf overrides a filter's enablement and enforcement
// at a single selector scope (environment/model_group/model/provider). It never
// changes priority, timeout, or hold bounds: those stay request-stable so a
// filter's recovery intent priority always matches its resolved registration
// priority.
type StreamGateFilterSelectorConf struct {
	Type string `mapstructure:"type" yaml:"type"`
	Key  string `mapstructure:"key"  yaml:"key"`
	// Enabled overrides the base enabled value at this selector. Omitted (nil)
	// inherits the base filter enabled value.
	Enabled *bool `mapstructure:"enabled" yaml:"enabled,omitempty"`
	// Enforcement overrides the base enforcement at this selector. Empty inherits
	// the base filter enforcement.
	Enforcement string `mapstructure:"enforcement" yaml:"enforcement,omitempty"`
}

// StreamGateFilterPolicyConf declares one semantic output-validation filter and
// its request-stable policy. Enablement and enforcement can be refined per
// environment/model/provider selector; priority, timeout, capability, and hold
// bounds are request-stable base values.
type StreamGateFilterPolicyConf struct {
	// Filter is the filter kind. It is unique within the Filters slice.
	Filter string `mapstructure:"filter" yaml:"filter"`
	// Enabled is the base enablement. Omitted (nil) defaults to true.
	Enabled *bool `mapstructure:"enabled" yaml:"enabled,omitempty"`
	// Enforcement is the base enforcement. Empty defaults to blocking.
	Enforcement string `mapstructure:"enforcement" yaml:"enforcement,omitempty"`
	// Capability is the provider capability id a candidate must advertise for
	// this filter to be admissible. Empty defaults to output.<filter>.
	Capability string `mapstructure:"capability" yaml:"capability,omitempty"`
	// HoldEvidenceRunes is the rolling-window evidence bound for the repeat
	// guard. Zero defaults to 500. Ignored by non-rolling filters.
	HoldEvidenceRunes int `mapstructure:"hold_evidence_runes" yaml:"hold_evidence_runes,omitempty"`
	// TimeoutMS is the per-evaluation timeout. Zero defaults to 5000.
	TimeoutMS int `mapstructure:"timeout_ms" yaml:"timeout_ms,omitempty"`
	// Priority is the evaluation/arbitration priority. Non-negative.
	Priority  int                            `mapstructure:"priority"  yaml:"priority,omitempty"`
	Selectors []StreamGateFilterSelectorConf `mapstructure:"selectors" yaml:"selectors,omitempty"`
}

// EffectiveEnabled reports the base enablement, defaulting to true when omitted.
func (f StreamGateFilterPolicyConf) EffectiveEnabled() bool {
	if f.Enabled == nil {
		return true
	}
	return *f.Enabled
}

// EffectiveEnforcement reports the base enforcement, defaulting to blocking.
func (f StreamGateFilterPolicyConf) EffectiveEnforcement() string {
	if strings.TrimSpace(f.Enforcement) == "" {
		return StreamGateFilterEnforcementBlocking
	}
	return f.Enforcement
}

// EffectiveCapability reports the required provider capability, defaulting to
// output.<filter> when omitted.
func (f StreamGateFilterPolicyConf) EffectiveCapability() string {
	if strings.TrimSpace(f.Capability) == "" {
		return "output." + f.Filter
	}
	return f.Capability
}

// EffectiveHoldEvidenceRunes reports the rolling-window bound, defaulting to 500.
func (f StreamGateFilterPolicyConf) EffectiveHoldEvidenceRunes() int {
	if f.HoldEvidenceRunes == 0 {
		return DefaultStreamGateFilterHoldEvidenceRunes
	}
	return f.HoldEvidenceRunes
}

// EffectiveTimeoutMS reports the per-evaluation timeout, defaulting to 5000.
func (f StreamGateFilterPolicyConf) EffectiveTimeoutMS() int {
	if f.TimeoutMS == 0 {
		return DefaultStreamGateFilterTimeoutMS
	}
	return f.TimeoutMS
}

// Validate checks a single filter policy for known kind/enforcement/selector
// values and in-range numeric bounds.
func (f StreamGateFilterPolicyConf) Validate() error {
	if _, ok := knownStreamGateFilterKinds[f.Filter]; !ok {
		return fmt.Errorf("stream_evidence_gate filter kind %q is not one of repeat_guard/schema_gate/provider_error", f.Filter)
	}
	if _, ok := knownStreamGateFilterEnforcements[f.EffectiveEnforcement()]; !ok {
		return fmt.Errorf("stream_evidence_gate filter %q enforcement %q must be blocking or observe_only", f.Filter, f.Enforcement)
	}
	if strings.TrimSpace(f.EffectiveCapability()) == "" {
		return fmt.Errorf("stream_evidence_gate filter %q capability must not be empty", f.Filter)
	}
	if runes := f.EffectiveHoldEvidenceRunes(); runes < 1 || runes > MaxStreamGateFilterHoldEvidenceRunes {
		return fmt.Errorf("stream_evidence_gate filter %q hold_evidence_runes must be between 1 and %d, got %d", f.Filter, MaxStreamGateFilterHoldEvidenceRunes, runes)
	}
	if ms := f.EffectiveTimeoutMS(); ms < 1 || ms > MaxStreamGateFilterTimeoutMS {
		return fmt.Errorf("stream_evidence_gate filter %q timeout_ms must be between 1 and %d, got %d", f.Filter, MaxStreamGateFilterTimeoutMS, ms)
	}
	if f.Priority < 0 {
		return fmt.Errorf("stream_evidence_gate filter %q priority must be non-negative, got %d", f.Filter, f.Priority)
	}
	for i, sel := range f.Selectors {
		if _, ok := knownStreamGateFilterSelectorTypes[sel.Type]; !ok {
			return fmt.Errorf("stream_evidence_gate filter %q selector %d type %q must be environment/model_group/model/provider", f.Filter, i, sel.Type)
		}
		if strings.TrimSpace(sel.Key) == "" {
			return fmt.Errorf("stream_evidence_gate filter %q selector %d key must not be empty", f.Filter, i)
		}
		if strings.TrimSpace(sel.Enforcement) != "" {
			if _, ok := knownStreamGateFilterEnforcements[sel.Enforcement]; !ok {
				return fmt.Errorf("stream_evidence_gate filter %q selector %d enforcement %q must be blocking or observe_only", f.Filter, i, sel.Enforcement)
			}
		}
	}
	return nil
}

// EffectiveEnvironment returns the deployment selector input, defaulting to
// dev when omitted.
func (s StreamEvidenceGateConf) EffectiveEnvironment() string {
	if strings.TrimSpace(s.Environment) == "" {
		return StreamGateEnvironmentDev
	}
	return s.Environment
}

// EffectiveMaxRequestFaultRecovery returns the effective request-total fault recovery limit.
// Omitted (nil) defaults to 3.
func (s StreamEvidenceGateConf) EffectiveMaxRequestFaultRecovery() int {
	if s.MaxRequestFaultRecovery == nil {
		return 3
	}
	return *s.MaxRequestFaultRecovery
}

// EffectiveMaxStrategyFaultRecovery returns the effective strategy fault recovery cap.
// Omitted (nil) inherits EffectiveMaxRequestFaultRecovery().
func (s StreamEvidenceGateConf) EffectiveMaxStrategyFaultRecovery() int {
	if s.MaxStrategyFaultRecovery == nil {
		return s.EffectiveMaxRequestFaultRecovery()
	}
	return *s.MaxStrategyFaultRecovery
}

// EffectiveMaxIngressSnapshotBytes returns the effective ingress snapshot bytes limit.
// Zero value (omitted) defaults to 16 MiB (16777216 bytes).
func (s StreamEvidenceGateConf) EffectiveMaxIngressSnapshotBytes() int {
	if s.MaxIngressSnapshotBytes == 0 {
		return 16 * 1024 * 1024
	}
	return s.MaxIngressSnapshotBytes
}

// Validate checks internal consistency and boundaries for StreamEvidenceGateConf.
func (s StreamEvidenceGateConf) Validate() error {
	effTotal := s.EffectiveMaxRequestFaultRecovery()
	environment := s.EffectiveEnvironment()
	if environment != StreamGateEnvironmentDev && environment != StreamGateEnvironmentDevCorp {
		return fmt.Errorf("stream_evidence_gate environment %q must be dev or dev-corp", s.Environment)
	}
	if effTotal < 0 || effTotal > 3 {
		return fmt.Errorf("max_request_fault_recovery must be between 0 and 3, got %d", effTotal)
	}

	effStrategy := s.EffectiveMaxStrategyFaultRecovery()
	if effStrategy < 0 {
		return fmt.Errorf("max_strategy_fault_recovery must not be negative, got %d", effStrategy)
	}
	if effStrategy > effTotal {
		return fmt.Errorf("max_strategy_fault_recovery (%d) must not exceed max_request_fault_recovery (%d)", effStrategy, effTotal)
	}
	for i, filter := range s.Filters {
		if s.Enabled && filter.Filter == StreamGateFilterRepeatGuard && filterCanResolveBlocking(filter) && (effTotal == 0 || effStrategy == 0) {
			return fmt.Errorf("stream_evidence_gate filters[%d]: blocking repeat_guard requires max_request_fault_recovery and max_strategy_fault_recovery to be at least 1", i)
		}
	}

	effIngress := s.EffectiveMaxIngressSnapshotBytes()
	maxAllowedIngress := 16 * 1024 * 1024
	if effIngress <= 0 || effIngress > maxAllowedIngress {
		return fmt.Errorf("max_ingress_snapshot_bytes must be between 1 and %d bytes (16 MiB), got %d", maxAllowedIngress, effIngress)
	}

	seenFilterKinds := make(map[string]struct{}, len(s.Filters))
	for i, filter := range s.Filters {
		if err := filter.Validate(); err != nil {
			return fmt.Errorf("stream_evidence_gate filters[%d]: %w", i, err)
		}
		if _, dup := seenFilterKinds[filter.Filter]; dup {
			return fmt.Errorf("stream_evidence_gate filters[%d]: duplicate filter kind %q", i, filter.Filter)
		}
		seenFilterKinds[filter.Filter] = struct{}{}
	}

	return nil
}

func filterCanResolveBlocking(filter StreamGateFilterPolicyConf) bool {
	if filter.EffectiveEnabled() && filter.EffectiveEnforcement() == StreamGateFilterEnforcementBlocking {
		return true
	}
	for _, selector := range filter.Selectors {
		enabled := filter.EffectiveEnabled()
		if selector.Enabled != nil {
			enabled = *selector.Enabled
		}
		if !enabled {
			continue
		}
		enforcement := filter.EffectiveEnforcement()
		if selector.Enforcement != "" {
			enforcement = selector.Enforcement
		}
		if enforcement == StreamGateFilterEnforcementBlocking {
			return true
		}
	}
	return false
}

// EdgeOpenAIProviderAuthConf configures request-time provider token forwarding.
// It only describes which caller header to forward and never stores raw token
// values in config.
type EdgeOpenAIProviderAuthConf struct {
	Enabled      bool   `mapstructure:"enabled"       yaml:"enabled,omitempty"`
	FromHeader   string `mapstructure:"from_header"   yaml:"from_header,omitempty"`
	TargetHeader string `mapstructure:"target_header" yaml:"target_header,omitempty"`
	Scheme       string `mapstructure:"scheme"        yaml:"scheme,omitempty"`
	Required     bool   `mapstructure:"required"      yaml:"required,omitempty"`
}

// OpenAIPrincipalTokenConf maps a hashed OpenAI-compatible bearer token to the
// IOP bearer-token-operation identity it authenticates as. Raw tokens are
// never stored in config; only the SHA-256 hash is persisted.
type OpenAIPrincipalTokenConf struct {
	TokenRef        string `mapstructure:"token_ref"         yaml:"token_ref"`
	TokenHashSHA256 string `mapstructure:"token_hash_sha256" yaml:"token_hash_sha256"`
	PrincipalRef    string `mapstructure:"principal_ref"     yaml:"principal_ref"`
	PrincipalAlias  string `mapstructure:"principal_alias"   yaml:"principal_alias,omitempty"`
}

// EdgeA2AConf configures the A2A input surface.
type EdgeA2AConf struct {
	Enabled     bool   `mapstructure:"enabled"      yaml:"enabled"`
	Listen      string `mapstructure:"listen"       yaml:"listen"`
	Path        string `mapstructure:"path"         yaml:"path"`
	NodeRef     string `mapstructure:"node"         yaml:"node"`
	Adapter     string `mapstructure:"adapter"      yaml:"adapter"`
	Target      string `mapstructure:"target"       yaml:"target"`
	SessionID   string `mapstructure:"session_id"   yaml:"session_id"`
	TimeoutSec  int    `mapstructure:"timeout_sec"  yaml:"timeout_sec"`
	BearerToken string `mapstructure:"bearer_token" yaml:"bearer_token"`
}

// EdgeConsoleConf configures the ops console target.
type EdgeConsoleConf struct {
	Adapter    string `mapstructure:"adapter"     yaml:"adapter"`
	Target     string `mapstructure:"target"      yaml:"target"`
	Model      string `mapstructure:"model"       yaml:"model"` // legacy alias for target
	SessionID  string `mapstructure:"session_id"  yaml:"session_id"`
	Background bool   `mapstructure:"background"  yaml:"background"`
	TimeoutSec int    `mapstructure:"timeout_sec" yaml:"timeout_sec"`
}

// ResolveTarget returns the resolved console target, preferring the explicit
// Target field, then the legacy Model alias.
func (c EdgeConsoleConf) ResolveTarget() string {
	if c.Target != "" {
		return c.Target
	}
	return c.Model
}

// LoggingConf configures structured logging output.
type LoggingConf struct {
	Level  string `mapstructure:"level"  yaml:"level"`
	Pretty bool   `mapstructure:"pretty" yaml:"pretty"`
	Path   string `mapstructure:"path"   yaml:"path"`
}

// MetricsConf configures the Prometheus metrics HTTP port.
type MetricsConf struct {
	Port int `mapstructure:"port" yaml:"port"`
}

// SQLiteConf configures the SQLite backend DSN.
type SQLiteConf struct {
	DSN string `mapstructure:"dsn" yaml:"dsn"`
}

// NodeInfo carries a node's stable identity.
type NodeInfo struct {
	ID   string `mapstructure:"id"   yaml:"id"`
	Name string `mapstructure:"name" yaml:"name"`
}
