package httpapi

import (
	"strings"
	"testing"

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

func TestBuildModelCatalogAggregatesSources(t *testing.T) {
	models := []store.PlatformModel{
		{
			ID:          "model-a",
			PlatformID:  "platform-a",
			ModelName:   "seedance",
			ModelAlias:  "Seedance-2.0",
			ModelType:   store.StringList{"image_generate"},
			DisplayName: "Seedance Source A",
			BillingConfig: map[string]any{
				"image": map[string]any{"basePrice": float64(10), "dynamicWeight": map[string]any{"1K": float64(1), "2K": float64(2)}},
			},
			RateLimitPolicy: map[string]any{
				"platformLimits": map[string]any{
					"max_request_per_minute":  60,
					"max_token_per_minute":    1000,
					"max_concurrent_requests": 2,
				},
			},
			PricingMode: "inherit_discount",
			Enabled:     true,
		},
		{
			ID:          "model-b",
			PlatformID:  "platform-b",
			ModelName:   "seedance",
			ModelAlias:  "Seedance-2.0",
			ModelType:   store.StringList{"image_generate"},
			DisplayName: "Seedance Source B",
			BillingConfig: map[string]any{
				"image": map[string]any{"basePrice": float64(10), "dynamicWeight": map[string]any{"1K": float64(1), "2K": float64(2)}},
			},
			RateLimitPolicy: map[string]any{
				"rpm":        40,
				"tpm":        2000,
				"concurrent": 3,
			},
			DiscountFactor: 0.8,
			PricingMode:    "custom",
			Enabled:        true,
		},
	}
	platforms := []store.Platform{
		{ID: "platform-a", Provider: "volces", Name: "火山引擎", Status: "enabled", Priority: 20, DefaultDiscountFactor: 1},
		{ID: "platform-b", Provider: "gemini", Name: "Gemini", Status: "enabled", Priority: 10, DefaultDiscountFactor: 1},
	}
	providers := []store.CatalogProvider{
		{ProviderKey: "volces", DisplayName: "火山引擎", IconPath: "volces.png"},
		{ProviderKey: "gemini", DisplayName: "Google Gemini", IconPath: "gemini.png"},
	}
	accessRules := []store.AccessRule{
		{SubjectType: "user_group", SubjectID: "group-vip", ResourceType: "platform", ResourceID: "platform-b", Effect: "allow", Status: "active"},
		{SubjectType: "user_group", SubjectID: "group-blocked", ResourceType: "platform", ResourceID: "platform-a", Effect: "deny", Status: "active"},
	}
	userGroups := []store.UserGroup{
		{ID: "group-vip", GroupKey: "vip", Name: "VIP 用户组"},
		{ID: "group-blocked", GroupKey: "blocked", Name: "Blocked 用户组"},
	}
	baseModels := []store.BaseModel{
		{ID: "", Metadata: map[string]any{"description": "高质量图像生成模型"}},
	}

	response := buildModelCatalog(models, platforms, providers, nil, accessRules, userGroups, baseModels)
	if response.Summary.ModelCount != 1 || response.Summary.SourceCount != 2 {
		t.Fatalf("unexpected summary: %+v", response.Summary)
	}
	item := response.Items[0]
	if item.SourceCount != 2 {
		t.Fatalf("expected merged source count, got %d", item.SourceCount)
	}
	if item.Source.Label != "2 个源" {
		t.Fatalf("expected source label to only show count, got %q", item.Source.Label)
	}
	if item.RateLimits.RPM == nil || *item.RateLimits.RPM != 100 {
		t.Fatalf("expected summed rpm 100, got %+v", item.RateLimits.RPM)
	}
	if item.RateLimits.TPM == nil || *item.RateLimits.TPM != 3000 {
		t.Fatalf("expected summed tpm 3000, got %+v", item.RateLimits.TPM)
	}
	if item.RateLimits.Concurrent == nil || *item.RateLimits.Concurrent != 5 {
		t.Fatalf("expected summed concurrency 5, got %+v", item.RateLimits.Concurrent)
	}
	if item.Permission.Label != "用户组 VIP 用户组；拒绝 Blocked 用户组" {
		t.Fatalf("expected permission label from access rules, got %q", item.Permission.Label)
	}
	if len(item.Permission.AllowGroups) != 1 || item.Permission.AllowGroups[0] != "VIP 用户组" {
		t.Fatalf("expected allow permission groups, got %+v", item.Permission.AllowGroups)
	}
	if len(item.Permission.DenyGroups) != 1 || item.Permission.DenyGroups[0] != "Blocked 用户组" {
		t.Fatalf("expected deny permission groups, got %+v", item.Permission.DenyGroups)
	}
	if item.Discount.Label != "80% - 无折扣" {
		t.Fatalf("expected friendly discount label, got %q", item.Discount.Label)
	}
	if len(item.ProviderKeys) != 2 {
		t.Fatalf("expected both providers on merged item, got %+v", item.ProviderKeys)
	}
	if !hasFilterCount(response.Filters.Providers, "volces", 1) || !hasFilterCount(response.Filters.Providers, "gemini", 1) {
		t.Fatalf("expected provider filters to count merged model for each provider: %+v", response.Filters.Providers)
	}
	if !hasFilterCount(response.Filters.Capabilities, "image_generate", 1) {
		t.Fatalf("expected image generation capability filter: %+v", response.Filters.Capabilities)
	}
	if got := item.Pricing.Lines[0]; got != "图像：1K 10 / 2K 20" {
		t.Fatalf("unexpected pricing line %q", got)
	}
}

func TestBuildModelCatalogKeepsDisplayNameSeparateFromCallAlias(t *testing.T) {
	models := []store.PlatformModel{
		{
			ID:                "seedream-pro",
			PlatformID:        "platform-volces",
			ModelName:         "Seedream-5.0-Pro",
			ProviderModelName: "doubao-seedream-5-0-pro-260628",
			ModelAlias:        "Seedream-5.0-Pro",
			ModelType:         store.StringList{"image_edit", "image_generate"},
			DisplayName:       "Seedream 5.0 Pro",
			Enabled:           true,
		},
	}
	platforms := []store.Platform{
		{ID: "platform-volces", Provider: "volces", Name: "火山引擎", Status: "enabled"},
	}
	baseModels := []store.BaseModel{
		{
			ID:                "base-seedream-pro",
			CanonicalModelKey: "seedream:seedream-5.0-pro",
			InvocationName:    "seedream-5.0-pro",
			ProviderModelName: "doubao-seedream-5-0-pro-260628",
			DisplayName:       "Seedream 5.0 Pro",
			Status:            "active",
		},
	}
	models[0].BaseModelID = "base-seedream-pro"

	response := buildModelCatalog(models, platforms, nil, nil, nil, nil, baseModels)
	if len(response.Items) != 1 {
		t.Fatalf("expected one Seedream Pro catalog item, got %+v", response.Items)
	}
	item := response.Items[0]
	if item.Alias != "seedream-5.0-pro" || item.DisplayName != "Seedream 5.0 Pro" || item.ModelName != "seedream-5.0-pro" {
		t.Fatalf("catalog should separate the call alias from the display name, got %+v", item)
	}
	if strings.Contains(item.Alias, " ") {
		t.Fatalf("Seedream Pro call alias must not contain spaces, got %q", item.Alias)
	}
}

func TestBuildModelCatalogAggregatesByInvocationNameNotDisplayName(t *testing.T) {
	models := []store.PlatformModel{
		{ID: "official-a", PlatformID: "platform-a", BaseModelID: "base-a", ModelName: "provider/a", ModelType: store.StringList{"text_generate"}, DisplayName: "同名营销标题", Enabled: true},
		{ID: "official-b", PlatformID: "platform-b", BaseModelID: "base-b", ModelName: "provider/b", ModelType: store.StringList{"text_generate"}, DisplayName: "同名营销标题", Enabled: true},
		{ID: "official-a-mirror", PlatformID: "platform-b", BaseModelID: "base-a-mirror", ModelName: "mirror/a", ModelType: store.StringList{"text_generate"}, DisplayName: "另一个展示标题", Enabled: true},
	}
	platforms := []store.Platform{
		{ID: "platform-a", Provider: "provider-a", Status: "enabled"},
		{ID: "platform-b", Provider: "provider-b", Status: "enabled"},
	}
	baseModels := []store.BaseModel{
		{ID: "base-a", CanonicalModelKey: "provider-a:official-a", InvocationName: "official-a", DisplayName: "模型 A", Status: "active"},
		{ID: "base-a-mirror", CanonicalModelKey: "provider-b:official-a", InvocationName: "official-a", DisplayName: "模型 A 镜像", Status: "active"},
		{ID: "base-b", CanonicalModelKey: "provider-b:official-b", InvocationName: "official-b", DisplayName: "同名营销标题", Status: "active"},
	}

	response := buildModelCatalog(models, platforms, nil, nil, nil, nil, baseModels)
	if response.Summary.ModelCount != 2 || response.Summary.SourceCount != 3 {
		t.Fatalf("expected two official identities and three sources, got %+v", response.Summary)
	}
	for _, item := range response.Items {
		if item.ModelName == "official-a" && item.SourceCount != 2 {
			t.Fatalf("official-a mirrors should aggregate into one card: %+v", item)
		}
	}
}

func TestBuildModelCatalogExcludesDeprecatedBaseModels(t *testing.T) {
	models := []store.PlatformModel{
		{ID: "preview", PlatformID: "platform", BaseModelID: "base-preview", ModelName: "gemini-preview", ModelType: store.StringList{"text_generate"}, Enabled: true},
	}
	platforms := []store.Platform{{ID: "platform", Provider: "gemini", Status: "enabled"}}
	baseModels := []store.BaseModel{{ID: "base-preview", InvocationName: "gemini-preview", Status: "deprecated"}}

	response := buildModelCatalog(models, platforms, nil, nil, nil, nil, baseModels)
	if response.Summary.ModelCount != 0 || response.Summary.SourceCount != 0 {
		t.Fatalf("deprecated base models must not create new catalog cards: %+v", response.Summary)
	}
}

func TestBuildModelCatalogUsesBaseModelProviderForProviderFilters(t *testing.T) {
	models := []store.PlatformModel{
		{
			ID:          "glm-volces",
			PlatformID:  "platform-volces",
			BaseModelID: "base-glm",
			ModelName:   "glm-4.7",
			ModelAlias:  "GLM-4.7",
			ModelType:   store.StringList{"text_generate"},
			DisplayName: "GLM-4.7",
			Enabled:     true,
		},
		{
			ID:          "glm-zhipu",
			PlatformID:  "platform-zhipu",
			BaseModelID: "base-glm",
			ModelName:   "glm-4.7",
			ModelAlias:  "GLM-4.7",
			ModelType:   store.StringList{"text_generate"},
			DisplayName: "GLM-4.7",
			Enabled:     true,
		},
	}
	platforms := []store.Platform{
		{ID: "platform-volces", Provider: "volces-openai", Name: "火山引擎(OpenAI兼容)", Status: "enabled"},
		{ID: "platform-zhipu", Provider: "zhipu-openai", Name: "智谱官方", Status: "enabled"},
	}
	providers := []store.CatalogProvider{
		{ProviderKey: "volces-openai", DisplayName: "火山引擎(OpenAI兼容)", IconPath: "volces.png"},
		{ProviderKey: "zhipu-openai", DisplayName: "智谱AI", IconPath: "zhipu.png"},
	}
	baseModels := []store.BaseModel{
		{ID: "base-glm", ProviderKey: "zhipu-openai", ProviderModelName: "glm-4.7", ModelAlias: "GLM-4.7"},
	}

	response := buildModelCatalog(models, platforms, providers, nil, nil, nil, baseModels)
	if response.Summary.ModelCount != 1 || response.Summary.SourceCount != 2 {
		t.Fatalf("unexpected summary: %+v", response.Summary)
	}
	item := response.Items[0]
	if len(item.ProviderKeys) != 1 || item.ProviderKeys[0] != "zhipu-openai" {
		t.Fatalf("expected model provider zhipu-openai only, got %+v", item.ProviderKeys)
	}
	if len(item.Providers) != 1 || item.Providers[0].Name != "智谱AI" || item.Providers[0].SourceCount != 2 {
		t.Fatalf("expected provider summary to aggregate both sources under model provider, got %+v", item.Providers)
	}
	if !hasFilterCount(response.Filters.Providers, "zhipu-openai", 1) {
		t.Fatalf("expected zhipu provider filter count 1, got %+v", response.Filters.Providers)
	}
	if hasFilterCount(response.Filters.Providers, "volces-openai", 1) {
		t.Fatalf("did not expect platform provider in model provider filters: %+v", response.Filters.Providers)
	}
}

func TestBuildModelCatalogIncludes3DModelCapabilityFilter(t *testing.T) {
	models := []store.PlatformModel{
		{
			ID:          "tripo-image-to-model",
			PlatformID:  "platform-tripo",
			ModelName:   "tripo-3d",
			ModelAlias:  "Tripo 3D",
			ModelType:   store.StringList{"image_to_model"},
			DisplayName: "Tripo 3D",
			Enabled:     true,
		},
	}
	platforms := []store.Platform{
		{ID: "platform-tripo", Provider: "tripo3d", Name: "Tripo3D", Status: "enabled"},
	}

	response := buildModelCatalog(models, platforms, nil, nil, nil, nil, nil)
	if !hasFilterCount(response.Filters.Capabilities, "image_to_model", 1) {
		t.Fatalf("expected 3D model capability filter, got %+v", response.Filters.Capabilities)
	}
	if hasFilterCount(response.Filters.Capabilities, "image_generate", 1) {
		t.Fatalf("did not expect image_to_model to be classified as image: %+v", response.Filters.Capabilities)
	}
	if len(response.Items) != 1 || len(response.Items[0].CapabilityTags) != 1 || response.Items[0].CapabilityTags[0] != "图生 3D" {
		t.Fatalf("expected image-to-model capability tag, got %+v", response.Items)
	}
}

func TestBuildModelCatalogKeepsDistinctCapabilityFilters(t *testing.T) {
	models := []store.PlatformModel{
		{ID: "image-generate", PlatformID: "platform-a", ModelName: "image-generate", ModelAlias: "Image Generate", ModelType: store.StringList{"image_generate"}, DisplayName: "Image Generate", Enabled: true},
		{ID: "image-analysis", PlatformID: "platform-a", ModelName: "image-analysis", ModelAlias: "Image Analysis", ModelType: store.StringList{"image_analysis"}, DisplayName: "Image Analysis", Enabled: true},
		{ID: "video-generate", PlatformID: "platform-a", ModelName: "video-generate", ModelAlias: "Video Generate", ModelType: store.StringList{"video_generate"}, DisplayName: "Video Generate", Enabled: true},
		{ID: "video-understanding", PlatformID: "platform-a", ModelName: "video-understanding", ModelAlias: "Video Understanding", ModelType: store.StringList{"video_understanding"}, DisplayName: "Video Understanding", Enabled: true},
		{ID: "omni", PlatformID: "platform-a", ModelName: "omni", ModelAlias: "Omni", ModelType: store.StringList{"omni"}, DisplayName: "Omni", Enabled: true},
		{ID: "omni-video", PlatformID: "platform-a", ModelName: "omni-video", ModelAlias: "Omni Video", ModelType: store.StringList{"omni_video"}, DisplayName: "Omni Video", Enabled: true},
	}
	platforms := []store.Platform{
		{ID: "platform-a", Provider: "test", Name: "测试平台", Status: "enabled"},
	}

	response := buildModelCatalog(models, platforms, nil, nil, nil, nil, nil)
	for _, value := range []string{"image_generate", "image_analysis", "video_generate", "video_understanding", "omni", "omni_video"} {
		if !hasFilterCount(response.Filters.Capabilities, value, 1) {
			t.Fatalf("expected distinct capability filter %s, got %+v", value, response.Filters.Capabilities)
		}
	}
	if hasFilterCount(response.Filters.Capabilities, "image", 2) || hasFilterCount(response.Filters.Capabilities, "video", 2) {
		t.Fatalf("did not expect broad image/video filters: %+v", response.Filters.Capabilities)
	}
}

func TestBuildModelCatalogOnlyUsesEnabledPlatformModels(t *testing.T) {
	models := []store.PlatformModel{
		{
			ID:          "enabled-source",
			PlatformID:  "platform-enabled",
			BaseModelID: "base-deepseek",
			ModelName:   "deepseek-chat",
			ModelAlias:  "DeepSeek Chat",
			ModelType:   store.StringList{"text_generate"},
			DisplayName: "DeepSeek Chat",
			Enabled:     true,
		},
		{
			ID:          "disabled-model",
			PlatformID:  "platform-enabled",
			BaseModelID: "base-deepseek",
			ModelName:   "deepseek-chat",
			ModelAlias:  "DeepSeek Chat",
			ModelType:   store.StringList{"text_generate"},
			DisplayName: "DeepSeek Chat",
			Enabled:     false,
		},
		{
			ID:          "disabled-platform",
			PlatformID:  "platform-disabled",
			BaseModelID: "base-deepseek",
			ModelName:   "deepseek-chat",
			ModelAlias:  "DeepSeek Chat",
			ModelType:   store.StringList{"text_generate"},
			DisplayName: "DeepSeek Chat",
			Enabled:     true,
		},
	}
	platforms := []store.Platform{
		{ID: "platform-enabled", Provider: "aliyun-bailian-openai", Name: "阿里云百炼(OpenAI兼容)", Status: "enabled"},
		{ID: "platform-disabled", Provider: "silicon-flow-openai", Name: "硅基流动", Status: "disabled"},
	}
	providers := []store.CatalogProvider{
		{ProviderKey: "deepseek-openai", DisplayName: "DeepSeek"},
		{ProviderKey: "aliyun-bailian-openai", DisplayName: "阿里云百炼(OpenAI兼容)"},
		{ProviderKey: "silicon-flow-openai", DisplayName: "硅基流动"},
	}
	baseModels := []store.BaseModel{
		{ID: "base-deepseek", ProviderKey: "deepseek-openai", ProviderModelName: "deepseek-chat", ModelAlias: "DeepSeek Chat"},
	}

	response := buildModelCatalog(models, platforms, providers, nil, nil, nil, baseModels)
	if response.Summary.ModelCount != 1 || response.Summary.SourceCount != 1 {
		t.Fatalf("expected only one enabled source, got %+v", response.Summary)
	}
	item := response.Items[0]
	if item.SourceCount != 1 || len(item.Sources) != 1 || item.Sources[0].ID != "enabled-source" {
		t.Fatalf("expected only enabled source in item, got sourceCount=%d sources=%+v", item.SourceCount, item.Sources)
	}
	if len(item.ProviderKeys) != 1 || item.ProviderKeys[0] != "deepseek-openai" {
		t.Fatalf("expected base model provider deepseek-openai, got %+v", item.ProviderKeys)
	}
	if !hasFilterCount(response.Filters.Providers, "deepseek-openai", 1) {
		t.Fatalf("expected DeepSeek provider filter, got %+v", response.Filters.Providers)
	}
	if hasFilterCount(response.Filters.Providers, "aliyun-bailian-openai", 1) || hasFilterCount(response.Filters.Providers, "silicon-flow-openai", 1) {
		t.Fatalf("did not expect platform provider filters: %+v", response.Filters.Providers)
	}
}

func TestBillingConfigLinesShowsTextInputAndOutputPricing(t *testing.T) {
	lines := billingConfigLines(map[string]any{
		"text_total": map[string]any{
			"basePrice": 0.01,
			"formulaConfig": map[string]any{
				"inputTokenPrice":  0.01,
				"outputTokenPrice": 0.03,
			},
		},
	})

	if len(lines) != 2 {
		t.Fatalf("expected input and output pricing lines, got %+v", lines)
	}
	if lines[0] != "输入 0.01/k tokens" {
		t.Fatalf("unexpected input pricing line %q", lines[0])
	}
	if lines[1] != "输出 0.03/k tokens" {
		t.Fatalf("unexpected output pricing line %q", lines[1])
	}
}

func TestBillingConfigLinesShowsVideoFiveSecondBasis(t *testing.T) {
	lines := billingConfigLines(map[string]any{
		"video": map[string]any{
			"basePrice":     float64(75),
			"dynamicWeight": map[string]any{"480p": float64(1), "720p": float64(2)},
		},
	})

	if len(lines) != 1 {
		t.Fatalf("expected one video pricing line, got %+v", lines)
	}
	if lines[0] != "视频：480p 75 / 720p 150（5秒基准）" {
		t.Fatalf("unexpected video pricing line %q", lines[0])
	}

	flatLines := billingConfigLines(map[string]any{"videoBase": float64(100)})
	if len(flatLines) != 1 || flatLines[0] != "视频：100 / 5秒基准" {
		t.Fatalf("unexpected flat video pricing line %+v", flatLines)
	}
}

func hasFilterCount(options []ModelCatalogFilterOption, value string, count int) bool {
	for _, option := range options {
		if option.Value == value && option.Count == count {
			return true
		}
	}
	return false
}
