package clients

import (
	"encoding/json"
	"os"
	"testing"

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

func TestQwen38MatchesCrossServiceBehaviorVectors(t *testing.T) {
	raw, err := os.ReadFile("testdata/qwen38-reasoning-vectors.json")
	if err != nil {
		t.Fatalf("read behavior vectors: %v", err)
	}
	var vectors []struct {
		Name     string         `json:"name"`
		Input    map[string]any `json:"input"`
		Expected map[string]any `json:"expected"`
	}
	if err := json.Unmarshal(raw, &vectors); err != nil {
		t.Fatalf("decode behavior vectors: %v", err)
	}
	candidate := store.RuntimeModelCandidate{Provider: "aliyun-bailian-openai", ProviderModelName: "qwen3.8-max-preview"}
	for _, vector := range vectors {
		t.Run(vector.Name, func(t *testing.T) {
			body := cloneBody(vector.Input)
			body["model"] = "qwen3.8-max-preview"
			applyOpenAIChatReasoningParams(body, candidate)
			for _, key := range []string{"enable_thinking", "reasoning_effort", "thinking_budget", "temperature"} {
				expected, expectedPresent := vector.Expected[key]
				actual, actualPresent := body[key]
				if expectedPresent != actualPresent || (expectedPresent && normalizedJSONScalar(actual) != normalizedJSONScalar(expected)) {
					t.Fatalf("%s mismatch: expected=%#v actual=%#v body=%+v", key, expected, actual, body)
				}
			}
		})
	}
}

func normalizedJSONScalar(value any) any {
	if number, ok := value.(int); ok {
		return float64(number)
	}
	return value
}

func TestQwen38MaxPreviewReasoningNormalization(t *testing.T) {
	tests := []struct {
		name     string
		body     map[string]any
		effort   any
		budget   any
		temp     any
		enabled  any
		noEffort bool
	}{
		{name: "none", body: map[string]any{"reasoning_effort": "none"}, effort: "low"},
		{name: "minimal", body: map[string]any{"reasoning_effort": "minimal"}, effort: "low"},
		{name: "medium", body: map[string]any{"reasoning_effort": "medium"}, effort: "medium"},
		{name: "high", body: map[string]any{"reasoning_effort": "high"}, effort: "xhigh"},
		{name: "max", body: map[string]any{"reasoning_effort": "max"}, effort: "xhigh"},
		{name: "unspecified", body: map[string]any{}, noEffort: true},
		{name: "forced on", body: map[string]any{"enable_thinking": false}, effort: "low"},
		{name: "budget wins", body: map[string]any{"reasoning_effort": "high", "thinking_budget": 999999}, budget: 262144, noEffort: true},
		{name: "zero budget", body: map[string]any{"reasoning_effort": "medium", "thinking_budget_tokens": 0}, budget: 0, noEffort: true},
		{name: "temperature floor", body: map[string]any{"temperature": 0.1}, temp: 0.6, noEffort: true},
	}
	candidate := store.RuntimeModelCandidate{
		Provider:          "aliyun-bailian-openai",
		ProviderModelName: "qwen3.8-max-preview",
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			body := cloneBody(test.body)
			body["model"] = "qwen3.8-max-preview"
			applyOpenAIChatReasoningParams(body, candidate)
			if body["enable_thinking"] != true {
				t.Fatalf("Qwen3.8 must always enable thinking: %+v", body)
			}
			if test.noEffort {
				if _, ok := body["reasoning_effort"]; ok {
					t.Fatalf("reasoning_effort must be omitted: %+v", body)
				}
			} else if body["reasoning_effort"] != test.effort {
				t.Fatalf("expected effort %#v, got %+v", test.effort, body)
			}
			if test.budget != nil && body["thinking_budget"] != test.budget {
				t.Fatalf("expected budget %#v, got %+v", test.budget, body)
			}
			if test.temp != nil && body["temperature"] != test.temp {
				t.Fatalf("expected temperature %#v, got %+v", test.temp, body)
			}
		})
	}
}

func TestGenericReasoningCapabilityUsesNearestTierAndIgnoresModeSwitch(t *testing.T) {
	body := map[string]any{"reasoning_effort": "medium"}
	applyOpenAIChatReasoningParams(body, store.RuntimeModelCandidate{
		Provider: "openai",
		Capabilities: map[string]any{
			"text_generate": map[string]any{
				"supportThinking":           true,
				"supportThinkingModeSwitch": false,
				"thinkingEffortLevels":      []any{"low", "high"},
			},
		},
	})
	if body["reasoning_effort"] != "low" {
		t.Fatalf("equidistant effort must choose lower tier, got %+v", body)
	}
}

func TestGenericReasoningKeepsUnknownAndRemovesExplicitUnsupported(t *testing.T) {
	unknown := map[string]any{"reasoning_effort": "high"}
	applyOpenAIChatReasoningParams(unknown, store.RuntimeModelCandidate{Provider: "openai"})
	if unknown["reasoning_effort"] != "high" {
		t.Fatalf("unknown capability should preserve effort: %+v", unknown)
	}
	unsupported := map[string]any{"reasoning_effort": "high"}
	applyOpenAIChatReasoningParams(unsupported, store.RuntimeModelCandidate{
		Provider: "openai",
		Capabilities: map[string]any{
			"text_generate": map[string]any{"supportThinking": false},
		},
	})
	if _, ok := unsupported["reasoning_effort"]; ok {
		t.Fatalf("explicit unsupported capability should remove effort: %+v", unsupported)
	}
}

func TestQwen38ResponsesReasoningNormalization(t *testing.T) {
	candidate := store.RuntimeModelCandidate{
		Provider:          "aliyun-bailian-openai",
		ProviderModelName: "qwen3.8-max-preview",
	}
	tests := []struct {
		name      string
		requested string
		expected  string
	}{
		{name: "none", requested: "none", expected: "low"},
		{name: "minimal", requested: "minimal", expected: "low"},
		{name: "medium", requested: "medium", expected: "medium"},
		{name: "high", requested: "high", expected: "xhigh"},
		{name: "max", requested: "max", expected: "xhigh"},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			body := map[string]any{
				"model":       "qwen3.8-max-preview",
				"reasoning":   map[string]any{"effort": test.requested, "summary": "auto"},
				"temperature": 0.1,
			}
			applyOpenAIResponsesReasoningParams(body, candidate)
			reasoning := body["reasoning"].(map[string]any)
			if reasoning["effort"] != test.expected || reasoning["summary"] != "auto" {
				t.Fatalf("unexpected Responses reasoning: %+v", body)
			}
			if body["temperature"] != 0.6 {
				t.Fatalf("Qwen3.8 Responses temperature was not floored: %+v", body)
			}
		})
	}
}

func TestQwen38ResponsesForcedThinkingAndDefault(t *testing.T) {
	candidate := store.RuntimeModelCandidate{Provider: "aliyun-bailian-openai", ProviderModelName: "qwen3.8-max-preview"}
	forced := map[string]any{"enable_thinking": false}
	applyOpenAIResponsesReasoningParams(forced, candidate)
	if forced["reasoning"].(map[string]any)["effort"] != "low" {
		t.Fatalf("disabled Qwen3.8 Responses request must become low: %+v", forced)
	}
	if _, ok := forced["enable_thinking"]; ok {
		t.Fatalf("Chat-only alias leaked into Responses body: %+v", forced)
	}
	defaulted := map[string]any{"reasoning": map[string]any{"summary": "auto"}}
	applyOpenAIResponsesReasoningParams(defaulted, candidate)
	reasoning := defaulted["reasoning"].(map[string]any)
	if _, ok := reasoning["effort"]; ok || reasoning["summary"] != "auto" {
		t.Fatalf("unspecified effort must preserve upstream xhigh default: %+v", defaulted)
	}
}
