package config

import (
	"fmt"
	"net/url"
	"strings"
)

// ProtocolDriver identifies the wire/stream semantics of a protocol profile.
type ProtocolDriver string

const (
	ProtocolDriverOpenAIChat        ProtocolDriver = "openai_chat"
	ProtocolDriverAnthropicMessages ProtocolDriver = "anthropic_messages"
	ProtocolDriverOpenAIResponses   ProtocolDriver = "openai_responses"
)

// validProtocolDrivers is the closed set of admissible driver values.
var validProtocolDrivers = map[ProtocolDriver]struct{}{
	ProtocolDriverOpenAIChat:        {},
	ProtocolDriverAnthropicMessages: {},
	ProtocolDriverOpenAIResponses:   {},
}

// ProtocolOperation is a named operation within a protocol profile.
type ProtocolOperation string

const (
	OperationModels          ProtocolOperation = "models"
	OperationChatCompletions ProtocolOperation = "chat_completions"
	OperationMessages        ProtocolOperation = "messages"
	OperationCountTokens     ProtocolOperation = "count_tokens"
	OperationResponses       ProtocolOperation = "responses"
)

// validProtocolOperations is the closed set of admissible operation ids.
var validProtocolOperations = map[ProtocolOperation]struct{}{
	OperationModels:          {},
	OperationChatCompletions: {},
	OperationMessages:        {},
	OperationCountTokens:     {},
	OperationResponses:       {},
}

// validOperationsByDriver restricts which operations each driver may declare.
var validOperationsByDriver = map[ProtocolDriver]map[ProtocolOperation]struct{}{
	ProtocolDriverOpenAIChat: {
		OperationModels:          {},
		OperationChatCompletions: {},
		OperationResponses:       {},
		OperationCountTokens:     {},
	},
	ProtocolDriverAnthropicMessages: {
		OperationModels:      {},
		OperationMessages:    {},
		OperationCountTokens: {},
	},
	ProtocolDriverOpenAIResponses: {
		OperationModels:      {},
		OperationResponses:   {},
		OperationCountTokens: {},
	},
}

// validProtocolCapabilities is the closed vocabulary shared by config,
// admission, and request preparation. Lifecycle capabilities are a separate
// provider concern and intentionally do not participate in this set.
var validProtocolCapabilities = map[string]struct{}{
	"models":       {},
	"chat":         {},
	"messages":     {},
	"responses":    {},
	"streaming":    {},
	"tool_calling": {},
	"count_tokens": {},
}

var operationCapability = map[ProtocolOperation]string{
	OperationModels:          "models",
	OperationChatCompletions: "chat",
	OperationMessages:        "messages",
	OperationCountTokens:     "count_tokens",
	OperationResponses:       "responses",
}

// TokenCounterMode describes how a model group's input tokens are counted.
type TokenCounterMode string

const (
	TokenCounterDeterministic TokenCounterMode = "deterministic"
	TokenCounterEstimate      TokenCounterMode = "estimate"
)

// ProtocolAuthConf declares how a profile authenticates to its upstream.
type ProtocolAuthConf struct {
	// Header is the request header name carrying the credential (e.g.
	// "Authorization" or "x-api-key").
	Header string `mapstructure:"header" yaml:"header,omitempty"`
	// Scheme is the auth scheme prefix (e.g. "Bearer"). Empty means the
	// raw header value is the credential.
	Scheme string `mapstructure:"scheme" yaml:"scheme,omitempty"`
}

// ProtocolEffortMappingConf declares how one provider operation represents
// IOP's normalized reasoning-effort levels. Levels may map multiple IOP grades
// to one provider grade when the provider exposes a smaller scale.
type ProtocolEffortMappingConf struct {
	Levels      map[string]string `mapstructure:"levels" yaml:"levels,omitempty"`
	WithTools   bool              `mapstructure:"with_tools" yaml:"with_tools,omitempty"`
	TokenBudget bool              `mapstructure:"token_budget" yaml:"token_budget,omitempty"`
	Wire        string            `mapstructure:"wire" yaml:"wire,omitempty"`
}

// ProtocolToolCallMappingConf declares provider-specific tool-call metadata
// normalization for one operation. It is separate from effort mapping because
// reasoning grades and opaque tool continuation metadata are independent wire
// concerns even when one provider uses both.
type ProtocolToolCallMappingConf struct {
	Wire string `mapstructure:"wire" yaml:"wire,omitempty"`
}

const (
	ProtocolEffortWireOpenAIChat       = "openai_chat"
	ProtocolEffortWireOpenAIResponses  = "openai_responses"
	ProtocolEffortWireAnthropicMessage = "anthropic_messages"
	ProtocolEffortWireGeminiChat       = "gemini_openai_chat"
)

var validProtocolEffortWires = map[string]struct{}{
	ProtocolEffortWireOpenAIChat:       {},
	ProtocolEffortWireOpenAIResponses:  {},
	ProtocolEffortWireAnthropicMessage: {},
	ProtocolEffortWireGeminiChat:       {},
}

const ProtocolToolCallWireGeminiChat = "gemini_openai_chat"

var validProtocolToolCallWires = map[string]struct{}{
	ProtocolToolCallWireGeminiChat: {},
}

// ProtocolNormalizationConf contains provider-wire normalization facts. It is
// intentionally operation-scoped: a provider may support reasoning with tools
// on Responses while rejecting the same semantic request on Chat Completions.
type ProtocolNormalizationConf struct {
	Effort    map[string]ProtocolEffortMappingConf   `mapstructure:"effort" yaml:"effort,omitempty"`
	ToolCalls map[string]ProtocolToolCallMappingConf `mapstructure:"tool_calls" yaml:"tool_calls,omitempty"`
}

// ProtocolProfileConf is the overlayable configuration of a protocol profile.
// It is the source of truth for endpoint, operation paths, auth, and
// capabilities before concrete resolution.
type ProtocolProfileConf struct {
	// Base is the optional id of a built-in/custom profile this overlay
	// extends. At most one base is allowed; cycles and unknown bases are
	// rejected at config load.
	Base string `mapstructure:"base" yaml:"base,omitempty"`
	// Driver is the protocol driver (openai_chat, anthropic_messages,
	// openai_responses).
	Driver ProtocolDriver `mapstructure:"driver" yaml:"driver,omitempty"`
	// BaseURL is the normalized upstream base URL (no trailing slash).
	BaseURL string `mapstructure:"base_url" yaml:"base_url,omitempty"`
	// Operations maps operation id to a relative or absolute URL path.
	// Absolute URLs are returned unchanged; relative paths are joined once
	// to BaseURL.
	Operations map[string]string `mapstructure:"operations" yaml:"operations,omitempty"`
	// Auth declares the upstream authentication header/scheme.
	Auth ProtocolAuthConf `mapstructure:"auth" yaml:"auth,omitempty"`
	// Capabilities lists the profile's declared capabilities (e.g.
	// "messages", "streaming", "tool_calling").
	Capabilities []string `mapstructure:"capabilities" yaml:"capabilities,omitempty"`
	// ModelMapping maps selected/canonical model ids to upstream model ids.
	ModelMapping map[string]string `mapstructure:"model_mapping" yaml:"model_mapping,omitempty"`
	// Extensions holds restricted profile-specific options that cannot be
	// expressed in the typed fields above.
	Extensions map[string]any `mapstructure:"extensions" yaml:"extensions,omitempty"`
	// Normalization maps IOP semantic request controls to provider operations.
	Normalization ProtocolNormalizationConf `mapstructure:"normalization" yaml:"normalization,omitempty"`
}

var identityReasoningEffortLevels = map[string]string{
	"none": "none", "low": "low", "medium": "medium",
	"high": "high", "xhigh": "xhigh", "max": "max",
}

var reasoningEffortOrder = []string{"none", "low", "medium", "high", "xhigh", "max"}

func reasoningEffortGradeIndex(level string) int {
	for index, candidate := range reasoningEffortOrder {
		if candidate == level {
			return index
		}
	}
	return -1
}

func identityEffortMapping(wire string, withTools, tokenBudget bool) ProtocolEffortMappingConf {
	levels := make(map[string]string, len(identityReasoningEffortLevels))
	for level, providerLevel := range identityReasoningEffortLevels {
		levels[level] = providerLevel
	}
	return ProtocolEffortMappingConf{Levels: levels, WithTools: withTools, TokenBudget: tokenBudget, Wire: wire}
}

// ConcreteProtocolProfile is the immutable, resolved snapshot of a protocol
// profile. It is what gets carried through the wire, Node config, and adapter
// resolution. It never contains a Base reference.
type ConcreteProtocolProfile struct {
	ID string
	ProtocolProfileConf
}

// TokenCounterConf declares how a model group's input tokens are counted
// without an upstream call.
type TokenCounterConf struct {
	// Mode is "deterministic" or "estimate".
	Mode TokenCounterMode `mapstructure:"mode" yaml:"mode,omitempty"`
	// Per1kInput is the estimated input tokens per 1k characters for
	// estimate mode.
	Per1kInput int `mapstructure:"per_1k_input" yaml:"per_1k_input,omitempty"`
}

// builtInProtocolProfiles is the process-owned catalog. Config normalization
// always clones from this private map, so callers cannot mutate runtime
// defaults through the exported compatibility snapshot below.
var builtInProtocolProfiles = map[string]ProtocolProfileConf{
	"openai": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.openai.com/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
			string(OperationResponses):       "/responses",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming", "tool_calling", "responses"},
		Normalization: ProtocolNormalizationConf{Effort: map[string]ProtocolEffortMappingConf{
			string(OperationChatCompletions): identityEffortMapping(ProtocolEffortWireOpenAIChat, false, false),
			string(OperationResponses):       identityEffortMapping(ProtocolEffortWireOpenAIResponses, true, false),
		}},
	},
	"gemini": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming", "tool_calling"},
		Normalization: ProtocolNormalizationConf{Effort: map[string]ProtocolEffortMappingConf{
			string(OperationChatCompletions): ProtocolEffortMappingConf{
				Wire:        ProtocolEffortWireGeminiChat,
				WithTools:   true,
				TokenBudget: true,
				Levels:      map[string]string{"low": "low", "medium": "medium", "high": "high"},
			},
		}, ToolCalls: map[string]ProtocolToolCallMappingConf{
			string(OperationChatCompletions): {Wire: ProtocolToolCallWireGeminiChat},
		}},
	},
	"anthropic": {
		Driver:  ProtocolDriverAnthropicMessages,
		BaseURL: "https://api.anthropic.com",
		Operations: map[string]string{
			string(OperationMessages):    "/v1/messages",
			string(OperationCountTokens): "/v1/messages/count_tokens",
		},
		Auth:         ProtocolAuthConf{Header: "x-api-key"},
		Capabilities: []string{"messages", "streaming", "tool_calling", "count_tokens"},
		Normalization: ProtocolNormalizationConf{Effort: map[string]ProtocolEffortMappingConf{
			string(OperationMessages): identityEffortMapping(ProtocolEffortWireAnthropicMessage, true, true),
		}},
	},
	"glm": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.z.ai/api/paas/v4",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming", "tool_calling"},
	},
	"glm_coding": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.z.ai/api/coding/paas/v4",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming", "tool_calling"},
	},
	"kimi": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.moonshot.cn/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming"},
	},
	"minimax_chat": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.minimax.io/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming"},
	},
	"minimax_messages": {
		Driver:  ProtocolDriverAnthropicMessages,
		BaseURL: "https://api.minimax.io/anthropic",
		Operations: map[string]string{
			string(OperationMessages): "/v1/messages",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"messages", "streaming"},
	},
	"mimo_chat": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.xiaomimimo.com/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming"},
	},
	"mimo_messages": {
		Driver:  ProtocolDriverAnthropicMessages,
		BaseURL: "https://api.xiaomimimo.com/anthropic",
		Operations: map[string]string{
			string(OperationMessages): "/v1/messages",
		},
		Auth:         ProtocolAuthConf{Header: "api-key"},
		Capabilities: []string{"messages", "streaming"},
	},
	"grok": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://api.x.ai/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming"},
	},
	"seulgi_chat": {
		Driver:  ProtocolDriverOpenAIChat,
		BaseURL: "https://seulgivibe.example.invalid/openai/v1",
		Operations: map[string]string{
			string(OperationModels):          "/models",
			string(OperationChatCompletions): "/chat/completions",
		},
		Auth:         ProtocolAuthConf{Header: "Authorization", Scheme: "Bearer"},
		Capabilities: []string{"models", "chat", "streaming"},
	},
	"seulgi_messages": {
		Driver:  ProtocolDriverAnthropicMessages,
		BaseURL: "https://seulgivibe.example.invalid/anthropic/v1",
		Operations: map[string]string{
			string(OperationMessages):    "/messages",
			string(OperationCountTokens): "/messages/count_tokens",
		},
		Auth:         ProtocolAuthConf{Header: "x-api-key"},
		Capabilities: []string{"messages", "streaming", "tool_calling", "count_tokens"},
	},
}

// BuiltInProtocolProfiles is a compatibility snapshot for callers that resolve
// profiles directly. Runtime config assembly uses a fresh private clone via
// BuiltInProtocolProfileCatalog and is therefore immune to caller mutation.
var BuiltInProtocolProfiles = cloneProfileCatalog(builtInProtocolProfiles)

// BuiltInProtocolProfileCatalog returns a fully independent built-in catalog.
func BuiltInProtocolProfileCatalog() map[string]ProtocolProfileConf {
	return cloneProfileCatalog(builtInProtocolProfiles)
}

func cloneProfileCatalog(src map[string]ProtocolProfileConf) map[string]ProtocolProfileConf {
	dst := make(map[string]ProtocolProfileConf, len(src))
	for id, profile := range src {
		dst[id] = deepCopyProfileConf(profile)
	}
	return dst
}

// legacyProviderTypeToProfile maps legacy provider type strings to built-in
// profile ids. This is used only when no explicit profile selector is set.
var legacyProviderTypeToProfile = map[string]string{
	"openai_api":        "openai",
	"vllm":              "openai",
	"vllm-mlx":          "openai",
	"lemonade":          "openai",
	"sglang":            "openai",
	"openai_compat":     "openai",
	"seulgivibe_claude": "seulgi_messages",
	"seulgivibe_openai": "seulgi_chat",
	"anthropic":         "anthropic",
}

// LegacyProviderTypeToProfile returns the built-in profile id that a legacy
// provider type normalizes to. Returns "" when no mapping exists.
func LegacyProviderTypeToProfile(providerType string) string {
	return legacyProviderTypeToProfile[strings.TrimSpace(providerType)]
}

// validateProfileOperation checks that an operation id is valid for the
// given driver.
func validateProfileOperation(driver ProtocolDriver, op string) error {
	ops, ok := validOperationsByDriver[driver]
	if !ok {
		return fmt.Errorf("profile driver %q is not recognized", driver)
	}
	opID := ProtocolOperation(op)
	if _, valid := validProtocolOperations[opID]; !valid {
		return fmt.Errorf("profile operation %q is not a known operation", op)
	}
	if _, valid := ops[opID]; !valid {
		return fmt.Errorf("profile operation %q is not supported for driver %q", op, driver)
	}
	return nil
}

// validateProfileURL validates that an operation path is a well-formed
// relative path or absolute URL.
func validateProfileURL(baseURL, opPath string) error {
	trimmed := strings.TrimSpace(opPath)
	if trimmed == "" {
		return fmt.Errorf("profile operation path must not be empty")
	}
	if strings.HasPrefix(trimmed, "http://") || strings.HasPrefix(trimmed, "https://") {
		parsed, err := url.Parse(trimmed)
		if err != nil {
			return fmt.Errorf("profile operation path %q is not a valid absolute URL: %w", trimmed, err)
		}
		if (parsed.Scheme != "http" && parsed.Scheme != "https") || parsed.Host == "" {
			return fmt.Errorf("profile operation path %q must be an absolute http(s) URL", trimmed)
		}
		return nil
	}
	if !strings.HasPrefix(trimmed, "/") {
		return fmt.Errorf("profile operation path %q must start with '/' for relative paths (leading slash required)", trimmed)
	}
	if _, err := url.Parse(baseURL); err != nil {
		return fmt.Errorf("profile base_url %q is not a valid URL: %w", baseURL, err)
	}
	return nil
}

// deepCopyProfileConf creates a deep copy of a ProtocolProfileConf, copying
// all maps and slices so mutations to the copy do not affect the original.
func deepCopyProfileConf(src ProtocolProfileConf) ProtocolProfileConf {
	dst := src
	if src.Operations != nil {
		dst.Operations = make(map[string]string, len(src.Operations))
		for k, v := range src.Operations {
			dst.Operations[k] = v
		}
	}
	if src.Capabilities != nil {
		dst.Capabilities = append([]string(nil), src.Capabilities...)
	}
	if src.ModelMapping != nil {
		dst.ModelMapping = make(map[string]string, len(src.ModelMapping))
		for k, v := range src.ModelMapping {
			dst.ModelMapping[k] = v
		}
	}
	if src.Extensions != nil {
		dst.Extensions = deepCopyExtensions(src.Extensions)
	}
	if src.Normalization.Effort != nil {
		dst.Normalization.Effort = make(map[string]ProtocolEffortMappingConf, len(src.Normalization.Effort))
		for operation, mapping := range src.Normalization.Effort {
			dst.Normalization.Effort[operation] = cloneEffortMapping(mapping)
		}
	}
	if src.Normalization.ToolCalls != nil {
		dst.Normalization.ToolCalls = make(map[string]ProtocolToolCallMappingConf, len(src.Normalization.ToolCalls))
		for operation, mapping := range src.Normalization.ToolCalls {
			dst.Normalization.ToolCalls[operation] = mapping
		}
	}
	return dst
}

func cloneEffortMapping(src ProtocolEffortMappingConf) ProtocolEffortMappingConf {
	dst := ProtocolEffortMappingConf{
		WithTools:   src.WithTools,
		TokenBudget: src.TokenBudget,
		Wire:        src.Wire,
	}
	if src.Levels != nil {
		dst.Levels = make(map[string]string, len(src.Levels))
		for level, providerLevel := range src.Levels {
			dst.Levels[level] = providerLevel
		}
	}
	return dst
}

// deepCopyExtensions deep-copies a map[string]any, handling nested maps and
// slices.
func deepCopyExtensions(src map[string]any) map[string]any {
	if src == nil {
		return nil
	}
	dst := make(map[string]any, len(src))
	for k, v := range src {
		dst[k] = deepCopyAny(v)
	}
	return dst
}

func deepCopyAny(v any) any {
	switch val := v.(type) {
	case map[string]any:
		return deepCopyExtensions(val)
	case map[string]string:
		out := make(map[string]string, len(val))
		for k, item := range val {
			out[k] = item
		}
		return out
	case []any:
		out := make([]any, len(val))
		for i, item := range val {
			out[i] = deepCopyAny(item)
		}
		return out
	case []string:
		return append([]string(nil), val...)
	default:
		return v
	}
}

// ResolveProtocolProfile resolves a provider's profile selector against the
// catalog (built-in + custom overlays), producing an immutable
// ConcreteProtocolProfile snapshot. It enforces:
//   - cycle detection (base chain must terminate)
//   - unknown base rejection
//   - driver conflict (overlay driver must match base driver)
//   - operation/auth/capability validation
//   - URL validation
//
// When selector is empty, legacyType is consulted for alias normalization.
// If neither resolves, the returned error explains the failure.
func ResolveProtocolProfile(selector, legacyType string, catalog map[string]ProtocolProfileConf) (ConcreteProtocolProfile, error) {
	id := strings.TrimSpace(selector)
	if id == "" {
		id = LegacyProviderTypeToProfile(legacyType)
	}
	if id == "" {
		return ConcreteProtocolProfile{}, fmt.Errorf("provider profile: no profile selector and no legacy type mapping for %q", legacyType)
	}

	visited := make(map[string]struct{})
	var resolve func(pid string) (ProtocolProfileConf, error)
	resolve = func(pid string) (ProtocolProfileConf, error) {
		if _, cycle := visited[pid]; cycle {
			return ProtocolProfileConf{}, fmt.Errorf("profile %q: base cycle detected", pid)
		}
		visited[pid] = struct{}{}

		base, ok := catalog[pid]
		if !ok {
			return ProtocolProfileConf{}, fmt.Errorf("profile %q: unknown profile id", pid)
		}

		if strings.TrimSpace(base.Base) != "" {
			parent, err := resolve(base.Base)
			if err != nil {
				return ProtocolProfileConf{}, err
			}
			merged := deepCopyProfileConf(parent)
			overlay := deepCopyProfileConf(base)
			merged, err = mergeProfileOverlay(merged, overlay)
			if err != nil {
				return ProtocolProfileConf{}, err
			}
			return merged, nil
		}
		return deepCopyProfileConf(base), nil
	}

	resolved, err := resolve(id)
	if err != nil {
		return ConcreteProtocolProfile{}, err
	}

	if err := validateConcreteProfile(id, resolved); err != nil {
		return ConcreteProtocolProfile{}, err
	}

	return ConcreteProtocolProfile{ID: id, ProtocolProfileConf: resolved}, nil
}

// mergeProfileOverlay merges an overlay onto a base profile. Overlay fields
// that are non-zero/non-empty replace base fields; nil/empty overlay fields
// inherit from the base. A driver conflict (overlay driver differs from base
// driver) returns an error.
func mergeProfileOverlay(base, overlay ProtocolProfileConf) (ProtocolProfileConf, error) {
	merged := base
	if strings.TrimSpace(string(overlay.Driver)) != "" {
		if overlay.Driver != base.Driver {
			return ProtocolProfileConf{}, fmt.Errorf("profile: driver conflict: base %q vs overlay %q", base.Driver, overlay.Driver)
		}
	}
	if strings.TrimSpace(overlay.BaseURL) != "" {
		merged.BaseURL = overlay.BaseURL
	}
	if len(overlay.Operations) > 0 {
		if merged.Operations == nil {
			merged.Operations = make(map[string]string)
		}
		for k, v := range overlay.Operations {
			merged.Operations[k] = v
		}
	}
	if overlay.Auth.Header != "" {
		merged.Auth.Header = overlay.Auth.Header
	}
	if overlay.Auth.Scheme != "" {
		merged.Auth.Scheme = overlay.Auth.Scheme
	}
	if len(overlay.Capabilities) > 0 {
		merged.Capabilities = overlay.Capabilities
	}
	if len(overlay.ModelMapping) > 0 {
		if merged.ModelMapping == nil {
			merged.ModelMapping = make(map[string]string)
		}
		for k, v := range overlay.ModelMapping {
			merged.ModelMapping[k] = v
		}
	}
	if len(overlay.Extensions) > 0 {
		if merged.Extensions == nil {
			merged.Extensions = make(map[string]any)
		}
		for k, v := range overlay.Extensions {
			merged.Extensions[k] = v
		}
	}
	if len(overlay.Normalization.Effort) > 0 {
		if merged.Normalization.Effort == nil {
			merged.Normalization.Effort = make(map[string]ProtocolEffortMappingConf)
		}
		for operation, mapping := range overlay.Normalization.Effort {
			merged.Normalization.Effort[operation] = cloneEffortMapping(mapping)
		}
	}
	if len(overlay.Normalization.ToolCalls) > 0 {
		if merged.Normalization.ToolCalls == nil {
			merged.Normalization.ToolCalls = make(map[string]ProtocolToolCallMappingConf)
		}
		for operation, mapping := range overlay.Normalization.ToolCalls {
			merged.Normalization.ToolCalls[operation] = mapping
		}
	}
	return merged, nil
}

// validateConcreteProfile validates a resolved concrete profile: driver is
// known, operations are valid for the driver, URLs are well-formed, and
// capabilities are non-empty.
func validateConcreteProfile(id string, p ProtocolProfileConf) error {
	if _, ok := validProtocolDrivers[p.Driver]; !ok {
		return fmt.Errorf("profile %q: driver %q is not one of openai_chat/anthropic_messages/openai_responses", id, p.Driver)
	}
	if strings.TrimSpace(p.BaseURL) == "" {
		return fmt.Errorf("profile %q: base_url must not be empty", id)
	}
	base, err := url.Parse(p.BaseURL)
	if err != nil {
		return fmt.Errorf("profile %q: base_url %q is not a valid URL: %w", id, p.BaseURL, err)
	}
	if (base.Scheme != "http" && base.Scheme != "https") || base.Host == "" {
		return fmt.Errorf("profile %q: base_url %q must be an absolute http(s) URL", id, p.BaseURL)
	}
	if len(p.Operations) == 0 {
		return fmt.Errorf("profile %q: operations must not be empty", id)
	}
	if len(p.Capabilities) == 0 {
		return fmt.Errorf("profile %q: capabilities must not be empty", id)
	}
	capabilities := make(map[string]struct{}, len(p.Capabilities))
	for _, capability := range p.Capabilities {
		capability = strings.TrimSpace(capability)
		if _, ok := validProtocolCapabilities[capability]; !ok {
			return fmt.Errorf("profile %q: capability %q is not recognized", id, capability)
		}
		if _, duplicate := capabilities[capability]; duplicate {
			return fmt.Errorf("profile %q: capability %q is duplicated", id, capability)
		}
		capabilities[capability] = struct{}{}
	}
	for op, path := range p.Operations {
		if err := validateProfileOperation(p.Driver, op); err != nil {
			return fmt.Errorf("profile %q: %w", id, err)
		}
		if err := validateProfileURL(p.BaseURL, path); err != nil {
			return fmt.Errorf("profile %q: operation %q: %w", id, op, err)
		}
		if required := operationCapability[ProtocolOperation(op)]; required != "" {
			if _, ok := capabilities[required]; !ok {
				return fmt.Errorf("profile %q: operation %q requires capability %q", id, op, required)
			}
		}
	}
	for operation, mapping := range p.Normalization.Effort {
		if _, ok := p.Operations[operation]; !ok {
			return fmt.Errorf("profile %q: effort normalization operation %q is not declared", id, operation)
		}
		if len(mapping.Levels) == 0 {
			return fmt.Errorf("profile %q: effort normalization operation %q has no levels", id, operation)
		}
		if _, ok := validProtocolEffortWires[mapping.Wire]; !ok {
			return fmt.Errorf("profile %q: effort normalization operation %q has invalid wire %q", id, operation, mapping.Wire)
		}
		for level, providerLevel := range mapping.Levels {
			levelIndex := reasoningEffortGradeIndex(level)
			if levelIndex < 0 {
				return fmt.Errorf("profile %q: effort normalization level %q is not recognized", id, level)
			}
			providerLevel = strings.TrimSpace(providerLevel)
			if providerLevel == "" {
				return fmt.Errorf("profile %q: effort normalization level %q has an empty provider value", id, level)
			}
			// Canonical provider grades may collapse downward, but an explicit
			// mapping must never silently upgrade the caller's requested grade.
			if providerIndex := reasoningEffortGradeIndex(providerLevel); providerIndex > levelIndex {
				return fmt.Errorf("profile %q: effort normalization level %q upgrades to %q", id, level, providerLevel)
			}
		}
	}
	for operation, mapping := range p.Normalization.ToolCalls {
		if _, ok := p.Operations[operation]; !ok {
			return fmt.Errorf("profile %q: tool-call normalization operation %q is not declared", id, operation)
		}
		if _, ok := validProtocolToolCallWires[mapping.Wire]; !ok {
			return fmt.Errorf("profile %q: tool-call normalization operation %q has invalid wire %q", id, operation, mapping.Wire)
		}
	}
	if p.Auth.Header == "" {
		return fmt.Errorf("profile %q: auth.header must not be empty", id)
	}
	if !validHTTPFieldName(p.Auth.Header) {
		return fmt.Errorf("profile %q: auth.header %q is not a valid HTTP header name", id, p.Auth.Header)
	}
	authHeader := strings.ToLower(p.Auth.Header)
	authScheme := strings.TrimSpace(p.Auth.Scheme)
	if authHeader == "authorization" && !strings.EqualFold(authScheme, "Bearer") {
		return fmt.Errorf("profile %q: Authorization auth requires Bearer scheme", id)
	}
	if authHeader != "authorization" && authScheme != "" {
		return fmt.Errorf("profile %q: auth header %q must use a raw credential without scheme", id, p.Auth.Header)
	}
	switch p.Driver {
	case ProtocolDriverOpenAIChat:
		if _, ok := capabilities["chat"]; !ok {
			return fmt.Errorf("profile %q: driver %q requires capability %q", id, p.Driver, "chat")
		}
		if _, ok := p.Operations[string(OperationChatCompletions)]; !ok {
			return fmt.Errorf("profile %q: driver %q requires operation %q", id, p.Driver, OperationChatCompletions)
		}
	case ProtocolDriverAnthropicMessages:
		if _, ok := capabilities["messages"]; !ok {
			return fmt.Errorf("profile %q: driver %q requires capability %q", id, p.Driver, "messages")
		}
		if _, ok := p.Operations[string(OperationMessages)]; !ok {
			return fmt.Errorf("profile %q: driver %q requires operation %q", id, p.Driver, OperationMessages)
		}
	case ProtocolDriverOpenAIResponses:
		if _, ok := capabilities["responses"]; !ok {
			return fmt.Errorf("profile %q: driver %q requires capability %q", id, p.Driver, "responses")
		}
		if _, ok := p.Operations[string(OperationResponses)]; !ok {
			return fmt.Errorf("profile %q: driver %q requires operation %q", id, p.Driver, OperationResponses)
		}
	}
	return nil
}

// validHTTPFieldName implements the RFC 9110 field-name token grammar. It is
// deliberately byte-oriented because HTTP field names are ASCII-only tokens.
func validHTTPFieldName(name string) bool {
	if name == "" {
		return false
	}
	for i := 0; i < len(name); i++ {
		c := name[i]
		if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') {
			continue
		}
		switch c {
		case '!', '#', '$', '%', '&', '\'', '*', '+', '-', '.', '^', '_', '`', '|', '~':
			continue
		default:
			return false
		}
	}
	return true
}

// ResolveOperationURL resolves an operation id to its full URL. Absolute
// operation URLs are returned unchanged; relative paths are joined once to
// the normalized base URL. Missing operations return a typed error.
func (p ConcreteProtocolProfile) ResolveOperationURL(op string) (string, error) {
	if strings.TrimSpace(op) == "" {
		return "", fmt.Errorf("protocol profile %q: operation must not be empty", p.ID)
	}
	if err := validateProfileOperation(p.Driver, op); err != nil {
		return "", fmt.Errorf("protocol profile %q: %w", p.ID, err)
	}
	path, ok := p.Operations[op]
	if !ok {
		return "", fmt.Errorf("protocol profile %q: operation %q is not supported", p.ID, op)
	}
	return resolveOperationURL(p.BaseURL, path)
}

// resolveOperationURL joins a base URL and operation path exactly once.
func resolveOperationURL(baseURL, opPath string) (string, error) {
	trimmed := strings.TrimSpace(opPath)
	if trimmed == "" {
		return "", fmt.Errorf("operation path must not be empty")
	}
	if strings.HasPrefix(trimmed, "http://") || strings.HasPrefix(trimmed, "https://") {
		return trimmed, nil
	}
	base, err := url.Parse(baseURL)
	if err != nil {
		return "", fmt.Errorf("base_url %q is not a valid URL: %w", baseURL, err)
	}
	operation, err := url.Parse(trimmed)
	if err != nil {
		return "", fmt.Errorf("operation path %q is not a valid URL path: %w", opPath, err)
	}
	basePath := strings.TrimRight(base.Path, "/")
	base.Path = basePath + operation.Path
	base.RawQuery = operation.RawQuery
	base.Fragment = operation.Fragment
	return base.String(), nil
}

// HasCapability reports whether the profile declares a capability.
func (p ConcreteProtocolProfile) HasCapability(cap string) bool {
	for _, c := range p.Capabilities {
		if c == cap {
			return true
		}
	}
	return false
}

// MapModel returns the upstream model id for a selected/canonical model. An
// absent mapping is an identity mapping.
func (p ConcreteProtocolProfile) MapModel(model string) string {
	if mapped := strings.TrimSpace(p.ModelMapping[model]); mapped != "" {
		return mapped
	}
	return model
}

// MapReasoningEffort maps one normalized IOP effort grade to the provider
// value for an operation. If the exact grade is unsupported, it selects the
// nearest explicitly supported lower grade. It never upgrades effort and
// rejects unsupported operation/tool combinations.
func (p ConcreteProtocolProfile) MapReasoningEffort(operation ProtocolOperation, level string, hasTools bool) (string, bool) {
	mapping, ok := p.Normalization.Effort[string(operation)]
	if !ok || (hasTools && !mapping.WithTools) {
		return "", false
	}
	requestedIndex := reasoningEffortGradeIndex(level)
	if requestedIndex < 0 {
		return "", false
	}
	for index := requestedIndex; index >= 0; index-- {
		mapped := strings.TrimSpace(mapping.Levels[reasoningEffortOrder[index]])
		if mapped != "" {
			return mapped, true
		}
	}
	return "", false
}

// EffortMapping returns an operation-scoped copy of the normalization facts.
func (p ConcreteProtocolProfile) EffortMapping(operation ProtocolOperation) (ProtocolEffortMappingConf, bool) {
	mapping, ok := p.Normalization.Effort[string(operation)]
	if !ok {
		return ProtocolEffortMappingConf{}, false
	}
	return cloneEffortMapping(mapping), true
}

// ToolCallMapping returns the operation-scoped provider tool-call
// normalization facts. The returned value contains no caller identity.
func (p ConcreteProtocolProfile) ToolCallMapping(operation ProtocolOperation) (ProtocolToolCallMappingConf, bool) {
	mapping, ok := p.Normalization.ToolCalls[string(operation)]
	return mapping, ok
}

// Clone returns a deep copy of the concrete profile.
func (p ConcreteProtocolProfile) Clone() ConcreteProtocolProfile {
	return ConcreteProtocolProfile{
		ID:                  p.ID,
		ProtocolProfileConf: deepCopyProfileConf(p.ProtocolProfileConf),
	}
}
