package runtime

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

const (
	chatCompletionsProfileOpenAIRequiredToolsV1 = "chat_completions_openai_required_tools_v1"
	chatCompletionsProfileDeepSeekV4ThinkingV1  = "chat_completions_deepseek_v4_thinking_v1"

	chatCompletionsProfileHeader = "X-AgentTeam-Chat-Completions-Compatibility-Profile"

	assistantToolCallReasoningOptional = "optional"
	assistantToolCallReasoningRequired = "required"
	assistantToolCallContentNullable   = "nullable"
	assistantToolCallContentString     = "non_null_string"
)

// ChatCompletionsCompatibilityProfile is the immutable MaaS-owned wire
// contract frozen for one Knowledge Q&A execution attempt.
type ChatCompletionsCompatibilityProfile struct {
	ID                                 string            `json:"id"`
	SupportedToolChoiceModes           []string          `json:"supported_tool_choice_modes"`
	ToolChoiceParameterAccepted        bool              `json:"tool_choice_parameter_accepted"`
	ParallelToolCallsParameterAccepted bool              `json:"parallel_tool_calls_parameter_accepted"`
	AssistantToolCallReasoningReplay   string            `json:"assistant_tool_call_reasoning_replay"`
	AssistantToolCallContent           string            `json:"assistant_tool_call_content"`
	OutputTokenField                   string            `json:"output_token_field"`
	SupportsStoreFalse                 bool              `json:"supports_store_false"`
	SupportsNOne                       bool              `json:"supports_n_one"`
	ReasoningEffortMap                 map[string]string `json:"reasoning_effort_map"`
}

var requiredChatCompletionsProfileFields = []string{
	"id",
	"supported_tool_choice_modes",
	"tool_choice_parameter_accepted",
	"parallel_tool_calls_parameter_accepted",
	"assistant_tool_call_reasoning_replay",
	"assistant_tool_call_content",
	"output_token_field",
	"supports_store_false",
	"supports_n_one",
	"reasoning_effort_map",
}

// UnmarshalJSON rejects partial or newer MaaS profile objects. Profile
// semantics are a fail-closed execution contract, not a forward-compatible
// display payload.
func (profile *ChatCompletionsCompatibilityProfile) UnmarshalJSON(data []byte) error {
	var fields map[string]json.RawMessage
	if err := json.Unmarshal(data, &fields); err != nil {
		return err
	}
	for _, field := range requiredChatCompletionsProfileFields {
		if _, ok := fields[field]; !ok {
			return fmt.Errorf("chat completions compatibility profile is missing field %q", field)
		}
	}

	type wireProfile ChatCompletionsCompatibilityProfile
	var decoded wireProfile
	decoder := json.NewDecoder(bytes.NewReader(data))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(&decoded); err != nil {
		return err
	}
	if err := ensureJSONEOF(decoder); err != nil {
		return err
	}
	*profile = ChatCompletionsCompatibilityProfile(decoded)
	return nil
}

var supportedChatCompletionsProfiles = map[string]ChatCompletionsCompatibilityProfile{
	chatCompletionsProfileOpenAIRequiredToolsV1: {
		ID:                                 chatCompletionsProfileOpenAIRequiredToolsV1,
		SupportedToolChoiceModes:           []string{"none", "auto", "required", "named"},
		ToolChoiceParameterAccepted:        true,
		ParallelToolCallsParameterAccepted: true,
		AssistantToolCallReasoningReplay:   assistantToolCallReasoningOptional,
		AssistantToolCallContent:           assistantToolCallContentNullable,
		OutputTokenField:                   "max_completion_tokens",
		SupportsStoreFalse:                 true,
		SupportsNOne:                       true,
		ReasoningEffortMap: map[string]string{
			"low": "low", "medium": "medium", "high": "high",
		},
	},
	chatCompletionsProfileDeepSeekV4ThinkingV1: {
		ID:                                 chatCompletionsProfileDeepSeekV4ThinkingV1,
		SupportedToolChoiceModes:           []string{"auto"},
		ToolChoiceParameterAccepted:        false,
		ParallelToolCallsParameterAccepted: false,
		AssistantToolCallReasoningReplay:   assistantToolCallReasoningRequired,
		AssistantToolCallContent:           assistantToolCallContentString,
		OutputTokenField:                   "max_tokens",
		SupportsStoreFalse:                 false,
		SupportsNOne:                       false,
		ReasoningEffortMap: map[string]string{
			"low": "high", "medium": "high", "high": "high",
			"xhigh": "max", "max": "max",
		},
	},
}

func lookupSupportedChatCompletionsProfile(id string) (ChatCompletionsCompatibilityProfile, bool) {
	profile, ok := supportedChatCompletionsProfiles[strings.TrimSpace(id)]
	if !ok {
		return ChatCompletionsCompatibilityProfile{}, false
	}
	return cloneChatCompletionsProfile(profile), true
}

func validateChatCompletionsProfile(profile ChatCompletionsCompatibilityProfile) error {
	expected, ok := lookupSupportedChatCompletionsProfile(profile.ID)
	if !ok {
		return fmt.Errorf("Knowledge Q&A MaaS compatibility profile is unsupported")
	}
	actualJSON, actualErr := json.Marshal(profile)
	expectedJSON, expectedErr := json.Marshal(expected)
	if actualErr != nil || expectedErr != nil || string(actualJSON) != string(expectedJSON) {
		return fmt.Errorf("Knowledge Q&A MaaS compatibility profile conflicts with the supported contract")
	}
	if !slices.Contains(profile.SupportedToolChoiceModes, "auto") {
		return fmt.Errorf("Knowledge Q&A MaaS compatibility profile cannot execute automatic tools")
	}
	return nil
}

func cloneChatCompletionsProfile(profile ChatCompletionsCompatibilityProfile) ChatCompletionsCompatibilityProfile {
	profile.SupportedToolChoiceModes = append([]string(nil), profile.SupportedToolChoiceModes...)
	profile.ReasoningEffortMap = cloneProfileStringMap(profile.ReasoningEffortMap)
	return profile
}

func cloneProfileStringMap(input map[string]string) map[string]string {
	result := make(map[string]string, len(input))
	for key, value := range input {
		result[key] = value
	}
	return result
}

func (profile ChatCompletionsCompatibilityProfile) mappedReasoningEffort(reasonLevel string) (string, error) {
	if err := validateChatCompletionsProfile(profile); err != nil {
		return "", err
	}
	mapped := strings.TrimSpace(profile.ReasoningEffortMap[strings.TrimSpace(reasonLevel)])
	if mapped == "" {
		return "", invalidRequest("Knowledge Q&A reasoning level is unsupported by the MaaS compatibility profile")
	}
	return mapped, nil
}

func (profile ChatCompletionsCompatibilityProfile) supportsKnowledgeQAFeatures(features []string) bool {
	if err := validateChatCompletionsProfile(profile); err != nil ||
		!containsString(features, "chat_completions.tools") ||
		!containsString(features, "chat_completions.reasoning_effort") {
		return false
	}
	if profile.ToolChoiceParameterAccepted &&
		(!containsString(profile.SupportedToolChoiceModes, "required") ||
			!containsString(features, "chat_completions.tool_choice")) {
		return false
	}
	return !profile.ParallelToolCallsParameterAccepted ||
		containsString(features, "chat_completions.parallel_tool_calls")
}
