// Package reasoning maps user/config intent to CloudWeGo Eino OpenAI ChatModel fields
// (ReasoningEffort, ExtraFields such as thinking / reasoning_effort / output_config).
package reasoning

import (
	"strings"

	"cyberstrike-ai/internal/config"

	einoopenai "github.com/cloudwego/eino-ext/components/model/openai"
)

// ClientIntent is optional per-request override from ChatRequest.reasoning.
type ClientIntent struct {
	Mode   string
	Effort string
}

type wireProfile int

const (
	wireNone wireProfile = iota
	wireClaude
	wireDeepseek
	wireOpenAI
	wireOutputConfig
)

// ApplyToEinoChatModelConfig merges reasoning-related options into cfg.
// Precondition: cfg already has APIKey, BaseURL, Model, HTTPClient set.
func ApplyToEinoChatModelConfig(cfg *einoopenai.ChatModelConfig, oa *config.OpenAIConfig, client *ClientIntent) {
	if cfg == nil || oa == nil {
		return
	}
	sr := &oa.Reasoning
	allowClient := sr.AllowClientReasoningEffective()
	mode := effectiveMode(sr, client, allowClient)

	// Claude (Anthropic): merge admin extras first; optional extended thinking maps to top-level `thinking`
	// (see internal/openai convertOpenAIToClaude). DeepSeek/OpenAI-style fields are not sent.
	if strings.EqualFold(strings.TrimSpace(oa.Provider), "claude") ||
		strings.EqualFold(strings.TrimSpace(oa.Provider), "anthropic") {
		if len(sr.ExtraRequestFields) > 0 {
			if cfg.ExtraFields == nil {
				cfg.ExtraFields = make(map[string]any)
			}
			for k, v := range sr.ExtraRequestFields {
				cfg.ExtraFields[k] = v
			}
		}
		if mode == "off" {
			return
		}
		applyClaudeExtendedThinking(cfg, mode, effectiveEffort(sr, client, allowClient), oa.Model)
		return
	}

	if mode == "off" {
		return
	}
	effort := effectiveEffort(sr, client, allowClient)
	prof := resolveWireProfile(oa, sr)

	// Admin-defined extra root fields (merged first; automatic keys may follow).
	if len(sr.ExtraRequestFields) > 0 {
		if cfg.ExtraFields == nil {
			cfg.ExtraFields = make(map[string]any)
		}
		for k, v := range sr.ExtraRequestFields {
			cfg.ExtraFields[k] = v
		}
	}

	switch prof {
	case wireClaude, wireNone:
		return
	case wireDeepseek:
		applyDeepseek(cfg, mode, effort)
	case wireOutputConfig:
		applyOutputConfigEffort(cfg, mode, effort)
	default: // wireOpenAI
		applyOpenAICompat(cfg, mode, effort)
	}
}

// applyClaudeExtendedThinking sets Anthropic Messages API `thinking` when absent from ExtraRequestFields.
// Uses adaptive + summarized display by default (per Anthropic guidance for Claude 4.x); Sonnet 3.7 uses enabled+budget.
func applyClaudeExtendedThinking(cfg *einoopenai.ChatModelConfig, mode, effort, model string) {
	if cfg == nil || mode == "off" {
		return
	}
	if cfg.ExtraFields == nil {
		cfg.ExtraFields = make(map[string]any)
	}
	if _, exists := cfg.ExtraFields["thinking"]; exists {
		return
	}
	m := strings.ToLower(strings.TrimSpace(model))
	thinking := map[string]any{
		"type":    "adaptive",
		"display": "summarized",
	}
	// Sonnet 3.7: manual extended thinking is the documented path.
	if strings.Contains(m, "claude-3-7-sonnet") || strings.Contains(m, "3-7-sonnet") || strings.Contains(m, "sonnet-3.7") {
		thinking = map[string]any{
			"type":          "enabled",
			"budget_tokens": 10000,
			"display":       "summarized",
		}
	}
	// Opus 4.7+: manual enabled+budget rejected — keep adaptive only.
	if strings.Contains(m, "opus-4-7") || strings.Contains(m, "opus-4.7") {
		thinking = map[string]any{
			"type":    "adaptive",
			"display": "summarized",
		}
	}
	_ = effort // reserved: map to Anthropic effort / output_config when API stabilizes in one place
	cfg.ExtraFields["thinking"] = thinking
}

func effectiveMode(sr *config.OpenAIReasoningConfig, client *ClientIntent, allowClient bool) string {
	server := strings.ToLower(strings.TrimSpace(sr.ModeEffective()))
	if server == "" || server == "default" {
		server = "auto"
	}
	if !allowClient || client == nil {
		return server
	}
	cm := strings.ToLower(strings.TrimSpace(client.Mode))
	if cm == "" || cm == "default" {
		return server
	}
	return cm
}

func effectiveEffort(sr *config.OpenAIReasoningConfig, client *ClientIntent, allowClient bool) string {
	se := normalizeEffort(sr.Effort)
	if !allowClient || client == nil {
		return se
	}
	ce := normalizeEffort(client.Effort)
	if ce != "" {
		return ce
	}
	return se
}

func normalizeEffort(s string) string {
	e := strings.ToLower(strings.TrimSpace(s))
	switch e {
	case "low", "medium", "high", "max":
		return e
	default:
		return ""
	}
}

func resolveWireProfile(oa *config.OpenAIConfig, sr *config.OpenAIReasoningConfig) wireProfile {
	if strings.EqualFold(strings.TrimSpace(oa.Provider), "claude") {
		return wireClaude
	}
	p := strings.ToLower(strings.TrimSpace(sr.ProfileEffective()))
	switch p {
	case "output_config", "output_config_effort":
		return wireOutputConfig
	case "openai", "openai_compat":
		return wireOpenAI
	case "deepseek", "deepseek_compat":
		return wireDeepseek
	case "auto", "":
		bu := strings.ToLower(oa.BaseURL)
		mo := strings.ToLower(oa.Model)
		if strings.Contains(bu, "deepseek") || strings.Contains(mo, "deepseek") {
			return wireDeepseek
		}
		return wireOpenAI
	default:
		return wireOpenAI
	}
}

func applyDeepseek(cfg *einoopenai.ChatModelConfig, mode, effort string) {
	// auto: enable thinking for DeepSeek line; on: same; auto without effort still opens thinking.
	if mode == "off" {
		return
	}
	if mode == "auto" || mode == "on" {
		if cfg.ExtraFields == nil {
			cfg.ExtraFields = make(map[string]any)
		}
		cfg.ExtraFields["thinking"] = map[string]any{"type": "enabled"}
	}
	if effort != "" {
		if cfg.ExtraFields == nil {
			cfg.ExtraFields = make(map[string]any)
		}
		cfg.ExtraFields["reasoning_effort"] = effortStringForAPI(effort)
	}
}

func applyOpenAICompat(cfg *einoopenai.ChatModelConfig, mode, effort string) {
	if mode == "auto" && effort == "" {
		return
	}
	e := effort
	if mode == "on" && e == "" {
		e = "medium"
	}
	if e == "" {
		return
	}
	if e == "max" {
		if cfg.ExtraFields == nil {
			cfg.ExtraFields = make(map[string]any)
		}
		cfg.ExtraFields["reasoning_effort"] = "max"
		return
	}
	switch e {
	case "low":
		cfg.ReasoningEffort = einoopenai.ReasoningEffortLevelLow
	case "medium":
		cfg.ReasoningEffort = einoopenai.ReasoningEffortLevelMedium
	case "high":
		cfg.ReasoningEffort = einoopenai.ReasoningEffortLevelHigh
	}
}

func applyOutputConfigEffort(cfg *einoopenai.ChatModelConfig, mode, effort string) {
	if mode == "auto" && effort == "" {
		return
	}
	e := effort
	if mode == "on" && e == "" {
		e = "high"
	}
	if e == "" {
		return
	}
	if cfg.ExtraFields == nil {
		cfg.ExtraFields = make(map[string]any)
	}
	cfg.ExtraFields["output_config"] = map[string]any{"effort": effortStringForAPI(e)}
}

func effortStringForAPI(e string) string {
	// Gateways expect lowercase strings; "max" kept as max.
	return strings.ToLower(strings.TrimSpace(e))
}
