package runner

import (
	"fmt"
	"math"
	"strings"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
)

const (
	volcesOutputTokenThreshold       = 10240
	volcesOutputInvalidParameterCode = "invalid_parameter"
)

type outputTokenLimitProcessor struct{}

func (outputTokenLimitProcessor) Name() string { return "OutputTokenLimitProcessor" }

func (outputTokenLimitProcessor) ShouldProcess(_ map[string]any, modelType string, context *paramProcessContext) bool {
	return context != nil && isOpenAITextGenerationKind(context.kind) && isTextOutputModelType(modelType) && isVolcesCandidate(context.candidate)
}

func (outputTokenLimitProcessor) Process(params map[string]any, modelType string, context *paramProcessContext) bool {
	modelMax, sourceType, capabilityValue, ok := candidateMaxOutputTokens(context.candidate, modelType)
	path := capabilityPath(sourceType, "max_output_tokens")
	if parameter, value, explicit := explicitOutputTokenParameter(context.kind, params); explicit {
		if _, valid := positiveInteger(value); !valid {
			return context.reject(
				"OutputTokenLimitProcessor",
				parameter,
				value,
				fmt.Sprintf("%s must be a positive integer", parameter),
				path,
				capabilityValue,
			)
		}
	}
	if !ok {
		return true
	}

	if context.kind == "chat.completions" {
		if value, explicit := nonNullParameter(params, "max_tokens"); explicit {
			if parsed, _ := positiveInteger(value); parsed > modelMax {
				return context.reject("OutputTokenLimitProcessor", "max_tokens", value, fmt.Sprintf("max_tokens must be a positive integer no greater than the selected Volcengine model limit (%d)", modelMax), path, capabilityValue)
			}
			return true
		}
		if _, explicit := nonNullParameter(params, "max_completion_tokens"); explicit {
			return true
		}
		value := defaultVolcesOutputTokens(modelMax)
		params["max_tokens"] = value
		context.recordChange(
			"OutputTokenLimitProcessor", "set", "max_tokens", nil, value,
			fmt.Sprintf("火山候选未显式设置输出上限：floor(%d/3)=%d，阈值=%d，注入候选级默认值。", modelMax, modelMax/3, volcesOutputTokenThreshold),
			path, capabilityValue,
		)
		return true
	}

	if value, explicit := nonNullParameter(params, "max_output_tokens"); explicit {
		if parsed, _ := positiveInteger(value); parsed > modelMax {
			return context.reject("OutputTokenLimitProcessor", "max_output_tokens", value, fmt.Sprintf("max_output_tokens must be a positive integer no greater than the selected Volcengine model limit (%d)", modelMax), path, capabilityValue)
		}
		return true
	}
	value := defaultVolcesOutputTokens(modelMax)
	params["max_output_tokens"] = value
	context.recordChange(
		"OutputTokenLimitProcessor", "set", "max_output_tokens", nil, value,
		fmt.Sprintf("火山候选未显式设置输出上限：floor(%d/3)=%d，阈值=%d，注入候选级默认值。", modelMax, modelMax/3, volcesOutputTokenThreshold),
		path, capabilityValue,
	)
	return true
}

func filterRuntimeCandidatesByOutputTokens(kind string, requestedModel string, modelType string, body map[string]any, candidates []store.RuntimeModelCandidate) ([]store.RuntimeModelCandidate, map[string]any, error) {
	if !isOpenAITextGenerationKind(kind) || !isTextOutputModelType(modelType) || len(candidates) == 0 {
		return candidates, nil, nil
	}
	hasVolcesCandidate := false
	for _, candidate := range candidates {
		if isVolcesCandidate(candidate) {
			hasVolcesCandidate = true
			break
		}
	}
	if !hasVolcesCandidate {
		return candidates, nil, nil
	}
	explicitParameter, explicitValue, hasExplicitParameter := explicitOutputTokenParameter(kind, body)
	explicitTokens := 0
	if hasExplicitParameter {
		var valid bool
		explicitTokens, valid = positiveInteger(explicitValue)
		if !valid {
			summary := map[string]any{
				"filter": "volces_output_token_limit", "kind": kind, "requestedModel": requestedModel, "modelType": modelType,
				"candidateCount": len(candidates), "supportedCandidateCount": 0, "filteredCandidateCount": len(candidates),
				"code": volcesOutputInvalidParameterCode, "reason": "invalid_output_token_parameter",
				"parameter": explicitParameter, "requestedValue": explicitValue,
			}
			message := fmt.Sprintf("%s must be a positive integer", explicitParameter)
			return nil, summary, &store.ModelCandidateUnavailableError{Code: volcesOutputInvalidParameterCode, Message: message, Details: summary}
		}
	}
	filtered := make([]store.RuntimeModelCandidate, 0, len(candidates))
	rejected := make([]map[string]any, 0)
	missingCapability := make([]map[string]any, 0)
	invalidExplicit := false
	for _, candidate := range candidates {
		if !isVolcesCandidate(candidate) {
			filtered = append(filtered, candidate)
			continue
		}
		modelMax, sourceType, raw, configured := candidateMaxOutputTokens(candidate, modelType)
		detail := map[string]any{
			"platformId": candidate.PlatformID, "platformKey": candidate.PlatformKey, "provider": candidate.Provider,
			"platformModelId": candidate.PlatformModelID, "providerModelName": candidate.ProviderModelName,
			"modelType": modelType, "capabilityPath": capabilityPath(sourceType, "max_output_tokens"), "capabilityValue": raw,
		}
		if !configured {
			detail["reason"] = "max_output_tokens_missing"
			missingCapability = append(missingCapability, detail)
			filtered = append(filtered, candidate)
			continue
		}
		if hasExplicitParameter {
			if explicitParameter != "max_completion_tokens" && explicitTokens > modelMax {
				detail["reason"] = "requested_output_tokens_exceed_capability"
				detail["parameter"] = explicitParameter
				detail["requestedValue"] = explicitValue
				invalidExplicit = true
				rejected = append(rejected, detail)
				continue
			}
		}
		filtered = append(filtered, candidate)
	}
	if len(rejected) == 0 && len(missingCapability) == 0 {
		return filtered, nil, nil
	}
	summary := map[string]any{
		"filter": "volces_output_token_limit", "kind": kind, "requestedModel": requestedModel, "modelType": modelType,
		"candidateCount": len(candidates), "supportedCandidateCount": len(filtered), "filteredCandidateCount": len(rejected),
		"missingCapabilityCandidateCount": len(missingCapability), "passthroughMissingCapability": len(missingCapability) > 0,
		"threshold": volcesOutputTokenThreshold, "formula": "third=floor(modelMaxOutputTokens/3); default=third>=10240?third:modelMaxOutputTokens",
		"rejectedCandidates": rejected, "missingCapabilityCandidates": missingCapability,
	}
	if len(filtered) > 0 {
		return filtered, summary, nil
	}
	if invalidExplicit {
		message := "请求的输出 token 上限超过所有可用火山引擎候选的模型能力"
		summary["code"] = volcesOutputInvalidParameterCode
		return nil, summary, &store.ModelCandidateUnavailableError{Code: volcesOutputInvalidParameterCode, Message: message, Details: summary}
	}
	return filtered, summary, nil
}

func defaultVolcesOutputTokens(modelMax int) int {
	third := modelMax / 3
	if third >= volcesOutputTokenThreshold {
		return third
	}
	return modelMax
}

func isVolcesCandidate(candidate store.RuntimeModelCandidate) bool {
	provider := strings.ToLower(strings.TrimSpace(candidate.Provider))
	baseURL := strings.ToLower(strings.TrimSpace(candidate.BaseURL))
	return provider == "volces-openai" || strings.Contains(baseURL, "volces.com") || strings.Contains(baseURL, "byteplus.com")
}

func candidateMaxOutputTokens(candidate store.RuntimeModelCandidate, modelType string) (int, string, any, bool) {
	capabilities := effectiveModelCapability(candidate)
	seen := map[string]struct{}{}
	for _, candidateType := range []string{candidate.ModelType, modelType, "text_generate"} {
		candidateType = strings.TrimSpace(candidateType)
		if candidateType == "" {
			continue
		}
		if _, ok := seen[candidateType]; ok {
			continue
		}
		seen[candidateType] = struct{}{}
		capability := capabilityForType(capabilities, candidateType)
		if capability == nil {
			continue
		}
		raw, exists := capability["max_output_tokens"]
		if !exists {
			continue
		}
		value, ok := positiveInteger(raw)
		return value, candidateType, raw, ok
	}
	return 0, firstNonEmptyString(candidate.ModelType, modelType, "text_generate"), nil, false
}

func positiveInteger(value any) (int, bool) {
	number := floatFromAny(value)
	if number <= 0 || math.Trunc(number) != number || number > float64(math.MaxInt) {
		return 0, false
	}
	return int(number), true
}

func nonNullParameter(body map[string]any, key string) (any, bool) {
	value, ok := body[key]
	return value, ok && value != nil
}

func explicitOutputTokenParameter(kind string, body map[string]any) (string, any, bool) {
	if kind == "responses" {
		value, ok := nonNullParameter(body, "max_output_tokens")
		return "max_output_tokens", value, ok
	}
	if value, ok := nonNullParameter(body, "max_tokens"); ok {
		return "max_tokens", value, true
	}
	value, ok := nonNullParameter(body, "max_completion_tokens")
	return "max_completion_tokens", value, ok
}

func isOpenAITextGenerationKind(kind string) bool {
	return kind == "chat.completions" || kind == "responses"
}

func isTextOutputModelType(modelType string) bool {
	switch strings.TrimSpace(modelType) {
	case "", "text_generate", "chat", "responses", "text":
		return true
	default:
		return false
	}
}

func mergeCandidateFilterSummaries(summaries ...map[string]any) map[string]any {
	nonEmpty := make([]any, 0, len(summaries))
	for _, summary := range summaries {
		if len(summary) > 0 {
			nonEmpty = append(nonEmpty, summary)
		}
	}
	if len(nonEmpty) == 0 {
		return nil
	}
	if len(nonEmpty) == 1 {
		return nonEmpty[0].(map[string]any)
	}
	return map[string]any{"filters": nonEmpty}
}
