package openai

import (
	"bytes"
	"encoding/json"
	"fmt"
	"strings"
)

func decodeChatCompletionRequest(dec *json.Decoder, req *chatCompletionRequest) error {
	var raw map[string]json.RawMessage
	if err := dec.Decode(&raw); err != nil {
		return fmt.Errorf("invalid JSON request")
	}
	for key := range raw {
		switch key {
		case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store", "think", "reasoning_effort", "thinking_token_budget", "include_reasoning":
		default:
			return fmt.Errorf("%s is not supported for /v1/chat/completions", key)
		}
	}
	normalized, err := json.Marshal(raw)
	if err != nil {
		return fmt.Errorf("invalid JSON request")
	}
	if err := json.Unmarshal(normalized, req); err != nil {
		return fmt.Errorf("invalid /v1/chat/completions request format")
	}
	if req.MaxTokens != nil && *req.MaxTokens <= 0 {
		return fmt.Errorf("max_tokens must be greater than zero")
	}
	if req.MaxCompletionTokens != nil && *req.MaxCompletionTokens <= 0 {
		return fmt.Errorf("max_completion_tokens must be greater than zero")
	}
	if req.Temperature != nil && (*req.Temperature < 0 || *req.Temperature > 2) {
		return fmt.Errorf("temperature must be between 0 and 2")
	}
	if req.TopP != nil && (*req.TopP < 0 || *req.TopP > 1) {
		return fmt.Errorf("top_p must be between 0 and 1")
	}
	if err := validateThinkControl(req); err != nil {
		return err
	}
	return nil
}

// chatCompletionEnvelope holds the routing-relevant fields decoded leniently
// from a /v1/chat/completions request body before strict normalization. Unknown
// fields are ignored so the provider tunnel passthrough can forward Codex/
// provider-specific payloads verbatim. Strict field validation stays on the
// normalized non-provider path.
type chatCompletionEnvelope struct {
	Model    string          `json:"model"`
	Metadata json.RawMessage `json:"metadata,omitempty"`
	Stream   bool            `json:"stream"`
}

// decodeChatCompletionEnvelope leniently extracts only the routing-relevant
// fields (model, metadata, stream) from a /v1/chat/completions request body.
// It uses json.Decoder to read only the first JSON value so caller-provided
// trailing payload (provider-specific extensions beyond the root object) does
// not invalidate the envelope; strict field validation stays on the normalized
// non-provider path.
func decodeChatCompletionEnvelope(rawBody []byte) (chatCompletionEnvelope, error) {
	var env chatCompletionEnvelope
	dec := json.NewDecoder(bytes.NewReader(rawBody))
	if err := dec.Decode(&env); err != nil {
		return chatCompletionEnvelope{}, fmt.Errorf("invalid JSON request")
	}
	return env, nil
}

// decodeChatCompletionRequestLenient decodes a /v1/chat/completions request
// body while preserving unknown top-level fields. Known-field syntax and
// validation are still enforced so malformed standard fields cannot reach the
// provider tunnel. This is the provider-pool ingress path.
func decodeChatCompletionRequestLenient(dec *json.Decoder, req *chatCompletionRequest) error {
	var raw map[string]json.RawMessage
	if err := dec.Decode(&raw); err != nil {
		return fmt.Errorf("invalid JSON request")
	}
	for key := range raw {
		switch key {
		case "model", "messages", "stream", "metadata", "max_tokens", "max_completion_tokens", "temperature", "top_p", "presence_penalty", "frequency_penalty", "seed", "stop", "response_format", "tools", "tool_choice", "parallel_tool_calls", "stream_options", "store", "think", "reasoning_effort", "thinking_token_budget", "include_reasoning":
		default:
			// Unknown fields are tolerated for provider-pool passthrough.
		}
	}
	normalized, err := json.Marshal(raw)
	if err != nil {
		return fmt.Errorf("invalid JSON request")
	}
	if err := json.Unmarshal(normalized, req); err != nil {
		return fmt.Errorf("invalid /v1/chat/completions request format")
	}
	// Reuse the same validation rules on known fields for consistency.
	if req.MaxTokens != nil && *req.MaxTokens <= 0 {
		return fmt.Errorf("max_tokens must be greater than zero")
	}
	if req.MaxCompletionTokens != nil && *req.MaxCompletionTokens <= 0 {
		return fmt.Errorf("max_completion_tokens must be greater than zero")
	}
	if req.Temperature != nil && (*req.Temperature < 0 || *req.Temperature > 2) {
		return fmt.Errorf("temperature must be between 0 and 2")
	}
	if req.TopP != nil && (*req.TopP < 0 || *req.TopP > 1) {
		return fmt.Errorf("top_p must be between 0 and 1")
	}
	if err := validateThinkControl(req); err != nil {
		return err
	}
	return nil
}

func promptFromMessages(messages []chatMessage) string {
	var b strings.Builder
	for _, msg := range messages {
		content := strings.TrimSpace(msg.Content)
		if content == "" {
			continue
		}
		role := strings.TrimSpace(msg.Role)
		if role == "" {
			role = "user"
		}
		if b.Len() > 0 {
			b.WriteString("\n")
		}
		b.WriteString(role)
		b.WriteString(": ")
		b.WriteString(content)
	}
	return b.String()
}
