package httpapi

import (
	"net/http"
	"sort"
	"strconv"
	"strings"

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

type ModelCatalogResponse struct {
	Items   []ModelCatalogItem  `json:"items"`
	Filters ModelCatalogFilters `json:"filters"`
	Summary ModelCatalogSummary `json:"summary"`
}

type ModelCatalogSummary struct {
	ModelCount  int `json:"modelCount"`
	SourceCount int `json:"sourceCount"`
}

type ModelCatalogFilters struct {
	Capabilities []ModelCatalogFilterOption `json:"capabilities"`
	Providers    []ModelCatalogFilterOption `json:"providers"`
}

type ModelCatalogFilterOption struct {
	Value    string `json:"value"`
	Label    string `json:"label"`
	Count    int    `json:"count"`
	IconPath string `json:"iconPath,omitempty"`
}

type ModelCatalogItem struct {
	ID             string                        `json:"id"`
	Alias          string                        `json:"alias"`
	DisplayName    string                        `json:"displayName"`
	ModelName      string                        `json:"modelName"`
	Description    string                        `json:"description,omitempty"`
	IconPath       string                        `json:"iconPath,omitempty"`
	ModelType      []string                      `json:"modelType"`
	CapabilityTags []string                      `json:"capabilityTags"`
	ProviderKeys   []string                      `json:"providerKeys"`
	Providers      []ModelCatalogProviderSummary `json:"providers"`
	SourceCount    int                           `json:"sourceCount"`
	Source         ModelCatalogText              `json:"source"`
	Discount       ModelCatalogText              `json:"discount"`
	RateLimits     ModelCatalogRateLimits        `json:"rateLimits"`
	Permission     ModelCatalogPermission        `json:"permission"`
	Pricing        ModelCatalogPricing           `json:"pricing"`
	Enabled        bool                          `json:"enabled"`
	StatusLabel    string                        `json:"statusLabel,omitempty"`
	Sources        []ModelCatalogSource          `json:"sources,omitempty"`
}

type ModelCatalogProviderSummary struct {
	Key         string `json:"key"`
	Name        string `json:"name"`
	IconPath    string `json:"iconPath,omitempty"`
	SourceCount int    `json:"sourceCount"`
}

type ModelCatalogSource struct {
	ID                string                 `json:"id"`
	PlatformID        string                 `json:"platformId,omitempty"`
	PlatformName      string                 `json:"platformName,omitempty"`
	ProviderKey       string                 `json:"providerKey"`
	ProviderName      string                 `json:"providerName"`
	ModelName         string                 `json:"modelName"`
	ProviderModelName string                 `json:"providerModelName,omitempty"`
	ModelAlias        string                 `json:"modelAlias,omitempty"`
	LegacyAliases     []string               `json:"legacyAliases,omitempty"`
	DisplayName       string                 `json:"displayName"`
	ModelType         []string               `json:"modelType"`
	Enabled           bool                   `json:"enabled"`
	RateLimits        ModelCatalogRateLimits `json:"rateLimits"`
}

type ModelCatalogText struct {
	Label string `json:"label"`
	Title string `json:"title,omitempty"`
}

type ModelCatalogPermission struct {
	Label       string   `json:"label"`
	Title       string   `json:"title,omitempty"`
	AllowGroups []string `json:"allowGroups,omitempty"`
	DenyGroups  []string `json:"denyGroups,omitempty"`
}

type ModelCatalogPricing struct {
	Lines []string `json:"lines"`
	Title string   `json:"title,omitempty"`
}

type ModelCatalogRateLimits struct {
	RPM        *float64 `json:"rpm,omitempty"`
	TPM        *float64 `json:"tpm,omitempty"`
	Concurrent *float64 `json:"concurrent,omitempty"`
	Label      string   `json:"label"`
	Title      string   `json:"title,omitempty"`
}

type catalogSource struct {
	model        store.PlatformModel
	platform     store.Platform
	provider     store.CatalogProvider
	baseModel    store.BaseModel
	providerKey  string
	providerName string
	description  string
	iconPath     string
	rateLimits   ModelCatalogRateLimits
}

type catalogGroup struct {
	key          string
	alias        string
	displayName  string
	modelName    string
	modelType    []string
	capabilities map[string]any
	sources      []catalogSource
	enabled      bool
}

// listModelCatalog godoc
// @Summary 列出模型目录
// @Description 聚合平台模型、基础模型、供应商、运行策略和访问规则，返回前端模型目录所需的过滤器、摘要和展示字段。
// @Tags model-catalog
// @Produce json
// @Security BearerAuth
// @Success 200 {object} ModelCatalogResponse
// @Failure 401 {object} ErrorEnvelope
// @Failure 500 {object} ErrorEnvelope
// @Router /api/v1/model-catalog [get]
func (s *Server) listModelCatalog(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	models, err := s.store.ListModels(ctx)
	if err != nil {
		s.logger.Error("list model catalog models failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	models = s.platformModelResponses(ctx, models)

	platforms, err := s.store.ListPlatforms(ctx)
	if err != nil {
		s.logger.Error("list model catalog platforms failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	providers, err := s.store.ListCatalogProviders(ctx)
	if err != nil {
		s.logger.Error("list model catalog providers failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	runtimePolicies, err := s.store.ListRuntimePolicySets(ctx)
	if err != nil {
		s.logger.Error("list model catalog runtime policies failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	accessRules, err := s.store.ListAccessRules(ctx)
	if err != nil {
		s.logger.Error("list model catalog access rules failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	userGroups, err := s.store.ListUserGroups(ctx)
	if err != nil {
		s.logger.Error("list model catalog user groups failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}
	baseModels, err := s.store.ListBaseModels(ctx)
	if err != nil {
		s.logger.Error("list model catalog base models failed", "error", err)
		writeError(w, http.StatusInternalServerError, "list model catalog failed")
		return
	}

	writeJSON(w, http.StatusOK, buildModelCatalog(models, platforms, providers, runtimePolicies, accessRules, userGroups, baseModels))
}

func buildModelCatalog(
	models []store.PlatformModel,
	platforms []store.Platform,
	providers []store.CatalogProvider,
	runtimePolicies []store.RuntimePolicySet,
	accessRules []store.AccessRule,
	userGroups []store.UserGroup,
	baseModels []store.BaseModel,
) ModelCatalogResponse {
	platformMap := mapPlatforms(platforms)
	providerMap := mapProviders(providers)
	runtimePolicyMap := mapRuntimePolicies(runtimePolicies)
	accessRuleGroups := mapAccessRuleGroups(accessRules, mapUserGroups(userGroups))
	baseModelMap := mapBaseModels(baseModels)

	groups := map[string]*catalogGroup{}
	sourceCount := 0
	for _, model := range models {
		platform := platformMap[model.PlatformID]
		baseModel := baseModelMap[model.BaseModelID]
		if !catalogSourceEnabled(model, platform, baseModel) {
			continue
		}
		providerKey := modelProviderKey(model, baseModel, platform)
		provider := providerMap[providerKey]
		providerName := firstNonEmpty(
			provider.DisplayName,
			stringValue(baseModel.Metadata["sourceProviderName"]),
			baseModel.ProviderKey,
			model.Provider,
			platform.Provider,
			platform.Name,
			model.PlatformName,
			providerKey,
		)
		description := modelDescription(model, baseModel)
		iconPath := modelIconPath(model, baseModel, provider)
		source := catalogSource{
			model:        model,
			platform:     platform,
			provider:     provider,
			baseModel:    baseModel,
			providerKey:  providerKey,
			providerName: providerName,
			description:  description,
			iconPath:     iconPath,
			rateLimits:   effectiveModelRateLimits(model, platform, runtimePolicyMap),
		}

		key := catalogIdentityKey(model, baseModel)
		current := groups[key]
		if current == nil {
			current = &catalogGroup{
				key:          key,
				alias:        firstNonEmpty(baseModel.InvocationName, model.ModelName),
				displayName:  firstNonEmpty(baseModel.DisplayName, model.DisplayName, baseModel.InvocationName, model.ModelName),
				modelName:    firstNonEmpty(baseModel.InvocationName, model.ModelName),
				modelType:    cloneStringSlice(model.ModelType),
				capabilities: cloneObject(model.Capabilities),
			}
			groups[key] = current
		} else {
			current.modelType = uniqueStrings(append(current.modelType, model.ModelType...))
			current.capabilities = mergeCatalogObjects(current.capabilities, model.Capabilities)
		}
		sourceCount++
		current.sources = append(current.sources, source)
		current.enabled = current.enabled || (model.Enabled && platform.Status == "enabled")
	}

	items := make([]ModelCatalogItem, 0, len(groups))
	for _, group := range groups {
		items = append(items, modelCatalogItem(group, accessRuleGroups))
	}
	sort.Slice(items, func(i, j int) bool {
		return strings.ToLower(items[i].DisplayName) < strings.ToLower(items[j].DisplayName)
	})

	return ModelCatalogResponse{
		Items: items,
		Filters: ModelCatalogFilters{
			Capabilities: modelCatalogCapabilityFilters(items),
			Providers:    modelCatalogProviderFilters(items),
		},
		Summary: ModelCatalogSummary{
			ModelCount:  len(items),
			SourceCount: sourceCount,
		},
	}
}

func catalogSourceEnabled(model store.PlatformModel, platform store.Platform, baseModel store.BaseModel) bool {
	return model.Enabled && platform.ID != "" && platform.Status == "enabled" && (baseModel.ID == "" || baseModel.Status == "" || baseModel.Status == "active")
}

func modelCatalogItem(group *catalogGroup, accessRuleGroups map[string]accessRuleGroup) ModelCatalogItem {
	sources := sortedCatalogSources(group.sources)
	primary := sources[0]
	providers := modelCatalogProviders(sources)
	description := primary.description
	if description == "" {
		for _, source := range sources {
			if source.description != "" {
				description = source.description
				break
			}
		}
	}
	iconPath := primary.iconPath
	if iconPath == "" {
		for _, source := range sources {
			if source.iconPath != "" {
				iconPath = source.iconPath
				break
			}
		}
	}

	itemSources := make([]ModelCatalogSource, 0, len(sources))
	for _, source := range sources {
		itemSources = append(itemSources, ModelCatalogSource{
			ID:                source.model.ID,
			PlatformID:        source.model.PlatformID,
			PlatformName:      firstNonEmpty(source.platform.Name, source.model.PlatformName),
			ProviderKey:       source.providerKey,
			ProviderName:      source.providerName,
			ModelName:         firstNonEmpty(source.baseModel.InvocationName, source.model.ModelName),
			ProviderModelName: source.model.ProviderModelName,
			ModelAlias:        firstNonEmpty(source.baseModel.InvocationName, source.model.ModelName),
			LegacyAliases:     cloneStringSlice(source.baseModel.LegacyAliases),
			DisplayName:       firstNonEmpty(source.baseModel.DisplayName, source.model.DisplayName, source.baseModel.InvocationName, source.model.ModelName),
			ModelType:         cloneStringSlice(source.model.ModelType),
			Enabled:           source.model.Enabled && source.platform.Status == "enabled",
			RateLimits:        source.rateLimits,
		})
	}

	return ModelCatalogItem{
		ID:             group.key,
		Alias:          group.alias,
		DisplayName:    group.displayName,
		ModelName:      group.modelName,
		Description:    description,
		IconPath:       iconPath,
		ModelType:      cloneStringSlice(group.modelType),
		CapabilityTags: capabilityTagsForGroup(group.modelType, group.capabilities),
		ProviderKeys:   modelCatalogProviderKeys(providers),
		Providers:      providers,
		SourceCount:    len(sources),
		Source:         sourceText(sources),
		Discount:       discountText(sources),
		RateLimits:     aggregateRateLimits(sources),
		Permission:     permissionText(sources, accessRuleGroups),
		Pricing:        pricingText(sources),
		Enabled:        group.enabled,
		StatusLabel:    statusText(sources),
		Sources:        itemSources,
	}
}

func sortedCatalogSources(sources []catalogSource) []catalogSource {
	out := append([]catalogSource(nil), sources...)
	sort.SliceStable(out, func(i, j int) bool {
		leftEnabled := out[i].model.Enabled && out[i].platform.Status == "enabled"
		rightEnabled := out[j].model.Enabled && out[j].platform.Status == "enabled"
		if leftEnabled != rightEnabled {
			return leftEnabled
		}
		leftPriority := out[i].platform.Priority
		rightPriority := out[j].platform.Priority
		if leftPriority == 0 {
			leftPriority = 9999
		}
		if rightPriority == 0 {
			rightPriority = 9999
		}
		if leftPriority != rightPriority {
			return leftPriority < rightPriority
		}
		return out[i].model.ModelName < out[j].model.ModelName
	})
	return out
}

func modelCatalogProviders(sources []catalogSource) []ModelCatalogProviderSummary {
	byKey := map[string]*ModelCatalogProviderSummary{}
	for _, source := range sources {
		key := source.providerKey
		if key == "" {
			key = "unknown"
		}
		current := byKey[key]
		if current == nil {
			current = &ModelCatalogProviderSummary{
				Key:      key,
				Name:     source.providerName,
				IconPath: source.provider.IconPath,
			}
			byKey[key] = current
		}
		current.SourceCount++
	}
	items := make([]ModelCatalogProviderSummary, 0, len(byKey))
	for _, provider := range byKey {
		items = append(items, *provider)
	}
	sort.Slice(items, func(i, j int) bool {
		return items[i].Name < items[j].Name
	})
	return items
}

func modelCatalogProviderKeys(providers []ModelCatalogProviderSummary) []string {
	keys := make([]string, 0, len(providers))
	for _, provider := range providers {
		keys = append(keys, provider.Key)
	}
	return keys
}

func sourceText(sources []catalogSource) ModelCatalogText {
	label := formatInt(len(sources)) + " 个源"
	return ModelCatalogText{
		Label: label,
		Title: label,
	}
}

func sourceTitles(sources []catalogSource) []string {
	titles := make([]string, 0, len(sources))
	for _, source := range sources {
		titles = append(titles, firstNonEmpty(source.platform.Name, source.providerName, source.providerKey)+": "+source.model.ModelName)
	}
	return titles
}

func discountText(sources []catalogSource) ModelCatalogText {
	values := make([]float64, 0, len(sources))
	titles := make([]string, 0, len(sources))
	for _, source := range sources {
		discount := effectiveDiscount(source.model, source.platform)
		value := discountLabel(discount)
		values = append(values, discount)
		titles = append(titles, firstNonEmpty(source.platform.Name, source.model.ModelName)+": "+discountTitle(value, discount))
	}
	return ModelCatalogText{
		Label: discountRangeLabel(values),
		Title: strings.Join(titles, "；"),
	}
}

func effectiveDiscount(model store.PlatformModel, platform store.Platform) float64 {
	if model.DiscountFactor > 0 {
		return model.DiscountFactor
	}
	if model.PricingMode == "inherit_discount" && platform.DefaultDiscountFactor > 0 {
		return platform.DefaultDiscountFactor
	}
	return 1
}

func discountLabel(discount float64) string {
	if absFloat(discount-1) < 0.000001 {
		return "无折扣"
	}
	return formatPercent(discount)
}

func discountRangeLabel(values []float64) string {
	discounts := uniqueDiscountValues(values)
	if len(discounts) == 0 {
		return "无折扣"
	}
	if len(discounts) == 1 {
		return discountLabel(discounts[0])
	}
	return discountLabel(discounts[0]) + " - " + discountLabel(discounts[len(discounts)-1])
}

func uniqueDiscountValues(values []float64) []float64 {
	if len(values) == 0 {
		return nil
	}
	out := append([]float64(nil), values...)
	sort.Float64s(out)
	unique := out[:0]
	for _, value := range out {
		if len(unique) == 0 || absFloat(value-unique[len(unique)-1]) >= 0.000001 {
			unique = append(unique, value)
		}
	}
	return unique
}

func discountTitle(label string, discount float64) string {
	if label == "无折扣" {
		return "无折扣（100%）"
	}
	return formatPercent(discount)
}

func effectiveModelRateLimits(model store.PlatformModel, platform store.Platform, runtimePolicyMap map[string]store.RuntimePolicySet) ModelCatalogRateLimits {
	runtimePolicy := map[string]any(nil)
	if model.RuntimePolicySetID != "" {
		runtimePolicy = runtimePolicyMap[model.RuntimePolicySetID].RateLimitPolicy
	}
	policy := store.EffectiveRateLimitPolicy(store.EffectiveRateLimitPolicyInput{
		BasePolicy:            model.BaseRateLimitPolicy,
		PlatformPolicy:        platform.RateLimitPolicy,
		RuntimePolicy:         runtimePolicy,
		RuntimePolicyExplicit: model.RuntimePolicySetID != "",
		RuntimePolicyOverride: model.RuntimePolicyOverride,
		ModelPolicy:           model.RateLimitPolicy,
		ModelPolicyMode:       model.RateLimitPolicyMode,
	})
	policies := []rateLimitPolicySource{{policy: policy, authoritative: policy != nil}}
	limits := ModelCatalogRateLimits{
		RPM:        firstRateLimit(policies, "rpm"),
		TPM:        firstRateLimit(policies, "tpm_total"),
		Concurrent: firstRateLimit(policies, "concurrent"),
	}
	limits.Label = rateLimitLabel(limits)
	return limits
}

type rateLimitPolicySource struct {
	policy        map[string]any
	authoritative bool
}

func firstRateLimit(policies []rateLimitPolicySource, metric string) *float64 {
	for _, source := range policies {
		if value, ok := readRateLimit(source.policy, metric); ok {
			return floatPointer(value)
		}
		if source.authoritative {
			return floatPointer(0)
		}
	}
	return nil
}

func readRateLimit(policy map[string]any, metric string) (float64, bool) {
	if len(policy) == 0 {
		return 0, false
	}
	if rules, ok := policy["rules"].([]any); ok {
		for _, item := range rules {
			rule := objectValue(item)
			if stringValue(rule["metric"]) != metric {
				continue
			}
			if limit, ok := numberValue(rule["limit"]); ok {
				return limit, true
			}
		}
	}
	for _, key := range rateLimitKeys(metric) {
		if value, ok := numberValue(policy[key]); ok {
			return value, true
		}
	}
	platformLimits := objectValue(policy["platformLimits"])
	for _, key := range rateLimitKeys(metric) {
		if value, ok := numberValue(platformLimits[key]); ok {
			return value, true
		}
	}
	return 0, false
}

func rateLimitKeys(metric string) []string {
	switch metric {
	case "rpm":
		return []string{"rpm", "max_request_per_minute", "maxRequestsPerMinute"}
	case "tpm_total":
		return []string{"tpm_total", "tpm", "max_token_per_minute", "max_tokens_per_minute", "maxTokensPerMinute"}
	case "concurrent":
		return []string{"concurrent", "max_concurrent_requests", "maxConcurrentRequests"}
	default:
		return []string{metric}
	}
}

func aggregateRateLimits(sources []catalogSource) ModelCatalogRateLimits {
	var rpm, tpm, concurrent float64
	var hasRPM, hasTPM, hasConcurrent bool
	titles := make([]string, 0, len(sources))
	for _, source := range sources {
		limits := source.rateLimits
		if limits.RPM != nil {
			rpm += *limits.RPM
			hasRPM = true
		}
		if limits.TPM != nil {
			tpm += *limits.TPM
			hasTPM = true
		}
		if limits.Concurrent != nil {
			concurrent += *limits.Concurrent
			hasConcurrent = true
		}
		titles = append(titles, firstNonEmpty(source.platform.Name, source.model.ModelName)+": "+rateLimitLabel(limits))
	}
	out := ModelCatalogRateLimits{Title: strings.Join(titles, "；")}
	if hasRPM {
		out.RPM = &rpm
	}
	if hasTPM {
		out.TPM = &tpm
	}
	if hasConcurrent {
		out.Concurrent = &concurrent
	}
	out.Label = rateLimitLabel(out)
	return out
}

func rateLimitLabel(limits ModelCatalogRateLimits) string {
	return "RPM " + formatOptionalNumber(limits.RPM) + " / TPM " + formatOptionalNumber(limits.TPM) + " / 并发 " + formatOptionalNumber(limits.Concurrent)
}

func permissionText(sources []catalogSource, rules map[string]accessRuleGroup) ModelCatalogPermission {
	allow := map[string]bool{}
	deny := map[string]bool{}
	for _, source := range sources {
		collectAccessRuleGroups(rules["platform_model:"+source.model.ID], allow, deny)
		if source.model.PlatformID != "" {
			collectAccessRuleGroups(rules["platform:"+source.model.PlatformID], allow, deny)
		}
		collectPermissionConfigGroups(source.model.PermissionConfig, allow)
	}
	allowNames := sortedMapKeys(allow)
	denyNames := sortedMapKeys(deny)
	if len(allowNames) == 0 && len(denyNames) == 0 {
		return ModelCatalogPermission{Label: "不限制", Title: "没有用户组访问限制"}
	}
	labelParts := []string{}
	if len(allowNames) > 0 {
		label := "用户组 " + strings.Join(limitStrings(allowNames, 4), "、")
		if len(allowNames) > 4 {
			label += "等"
		}
		labelParts = append(labelParts, label)
	}
	if len(denyNames) > 0 {
		label := "拒绝 " + strings.Join(limitStrings(denyNames, 3), "、")
		if len(denyNames) > 3 {
			label += "等"
		}
		labelParts = append(labelParts, label)
	}
	titleParts := []string{}
	if len(allowNames) > 0 {
		titleParts = append(titleParts, "允许用户组："+strings.Join(allowNames, "、"))
	}
	if len(denyNames) > 0 {
		titleParts = append(titleParts, "拒绝用户组："+strings.Join(denyNames, "、"))
	}
	return ModelCatalogPermission{
		Label:       strings.Join(labelParts, "；"),
		Title:       strings.Join(titleParts, "；"),
		AllowGroups: allowNames,
		DenyGroups:  denyNames,
	}
}

type accessRuleGroup struct {
	allow map[string]bool
	deny  map[string]bool
}

func mapAccessRuleGroups(rules []store.AccessRule, userGroupMap map[string]string) map[string]accessRuleGroup {
	groups := map[string]accessRuleGroup{}
	for _, rule := range rules {
		if rule.Status != "active" || rule.SubjectType != "user_group" || (rule.ResourceType != "platform" && rule.ResourceType != "platform_model") {
			continue
		}
		key := rule.ResourceType + ":" + rule.ResourceID
		current := groups[key]
		if current.allow == nil {
			current.allow = map[string]bool{}
			current.deny = map[string]bool{}
		}
		groupName := firstNonEmpty(userGroupMap[rule.SubjectID], rule.SubjectID)
		if rule.Effect == "allow" {
			current.allow[groupName] = true
		}
		if rule.Effect == "deny" {
			current.deny[groupName] = true
		}
		groups[key] = current
	}
	return groups
}

func collectAccessRuleGroups(group accessRuleGroup, allow map[string]bool, deny map[string]bool) {
	for name := range group.allow {
		allow[name] = true
	}
	for name := range group.deny {
		deny[name] = true
	}
}

func collectPermissionConfigGroups(config map[string]any, allow map[string]bool) {
	if len(config) == 0 {
		return
	}
	for _, key := range []string{"userGroupNames", "userGroups", "allowedUserGroups", "allowedUserGroupNames"} {
		for _, value := range stringSliceValue(config[key]) {
			allow[value] = true
		}
	}
}

func pricingText(sources []catalogSource) ModelCatalogPricing {
	lineSets := make([]string, 0, len(sources))
	linesBySource := make([][]string, 0, len(sources))
	for _, source := range sources {
		lines := billingConfigLines(source.model.BillingConfig)
		if len(lines) == 0 {
			lines = []string{pricingModeLabel(source.model.PricingMode)}
		}
		linesBySource = append(linesBySource, lines)
		lineSets = append(lineSets, strings.Join(lines, "\n"))
	}
	uniqueSets := uniqueStrings(lineSets)
	lines := []string{}
	if len(uniqueSets) <= 1 {
		if len(linesBySource) > 0 {
			lines = append(lines, linesBySource[0]...)
		}
	} else {
		for index, source := range sources {
			label := firstNonEmpty(source.platform.Name, source.model.ModelName)
			for _, line := range linesBySource[index] {
				lines = append(lines, label+": "+line)
			}
		}
	}
	if len(lines) == 0 {
		lines = []string{"未配置具体价格"}
	}
	return ModelCatalogPricing{Lines: lines, Title: strings.Join(lines, "；")}
}

func billingConfigLines(config map[string]any) []string {
	if len(config) == 0 {
		return nil
	}
	if textPricingResource(config) {
		if lines := textPricingLines(config); len(lines) > 0 {
			return lines
		}
	}
	if basePrice, ok := numberValue(config["basePrice"]); ok {
		resourceType := firstNonEmpty(stringValue(config["resourceType"]), "model")
		return []string{priceLine(resourceType, map[string]any{"basePrice": basePrice, "dynamicWeight": config["dynamicWeight"], "unit": config["unit"]})}
	}
	lines := textPricingLines(config)
	for _, resource := range []string{"image", "image_edit", "video", "audio", "music", "digital_human", "model"} {
		if nested := objectValue(config[resource]); len(nested) > 0 {
			if _, ok := numberValue(nested["basePrice"]); ok {
				lines = append(lines, priceLine(resource, nested))
				continue
			}
		}
		for _, baseKey := range resourceBaseKeys(resource) {
			if value, ok := numberValue(config[baseKey]); ok {
				lines = append(lines, resourceBaseLine(resource, value))
				break
			}
		}
	}
	return uniqueStrings(lines)
}

func textPricingResource(config map[string]any) bool {
	switch strings.ToLower(stringValue(config["resourceType"])) {
	case "text", "text_total", "text_input", "text_cached_input", "text_output":
		return true
	default:
		return false
	}
}

func textPricingLines(config map[string]any) []string {
	if len(config) == 0 {
		return nil
	}
	inputPrice, hasInput := textPriceFromConfig(config, []string{"textInputPer1k", "textInput", "text_input", "inputTokenPrice", "inputPrice"})
	cachedInputPrice, hasCachedInput := textPriceFromConfig(config, []string{"textCachedInputPer1k", "textCachedInput", "text_cached_input", "cachedInputTokenPrice", "inputCacheHitTokenPrice", "cachedInputPrice"})
	outputPrice, hasOutput := textPriceFromConfig(config, []string{"textOutputPer1k", "textOutput", "text_output", "outputTokenPrice", "outputPrice"})

	for _, key := range []string{"formulaConfig", "formula_config"} {
		formulaConfig := objectValue(config[key])
		if !hasInput {
			inputPrice, hasInput = textPriceFromConfig(formulaConfig, []string{"inputTokenPrice", "textInputPer1k", "textInput", "text_input", "inputPrice"})
		}
		if !hasCachedInput {
			cachedInputPrice, hasCachedInput = textPriceFromConfig(formulaConfig, []string{"cachedInputTokenPrice", "textCachedInputPer1k", "textCachedInput", "text_cached_input", "inputCacheHitTokenPrice", "cachedInputPrice"})
		}
		if !hasOutput {
			outputPrice, hasOutput = textPriceFromConfig(formulaConfig, []string{"outputTokenPrice", "textOutputPer1k", "textOutput", "text_output", "outputPrice"})
		}
	}

	for _, resource := range []string{"text", "text_total"} {
		nested := objectValue(config[resource])
		if len(nested) == 0 {
			continue
		}
		if !hasInput {
			inputPrice, hasInput = textPriceFromConfig(nested, []string{"inputTokenPrice", "textInputPer1k", "textInput", "text_input", "inputPrice"})
		}
		if !hasCachedInput {
			cachedInputPrice, hasCachedInput = textPriceFromConfig(nested, []string{"cachedInputTokenPrice", "textCachedInputPer1k", "textCachedInput", "text_cached_input", "inputCacheHitTokenPrice", "cachedInputPrice"})
		}
		if !hasOutput {
			outputPrice, hasOutput = textPriceFromConfig(nested, []string{"outputTokenPrice", "textOutputPer1k", "textOutput", "text_output", "outputPrice"})
		}
		for _, key := range []string{"formulaConfig", "formula_config"} {
			formulaConfig := objectValue(nested[key])
			if !hasInput {
				inputPrice, hasInput = textPriceFromConfig(formulaConfig, []string{"inputTokenPrice", "textInputPer1k", "textInput", "text_input", "inputPrice"})
			}
			if !hasCachedInput {
				cachedInputPrice, hasCachedInput = textPriceFromConfig(formulaConfig, []string{"cachedInputTokenPrice", "textCachedInputPer1k", "textCachedInput", "text_cached_input", "inputCacheHitTokenPrice", "cachedInputPrice"})
			}
			if !hasOutput {
				outputPrice, hasOutput = textPriceFromConfig(formulaConfig, []string{"outputTokenPrice", "textOutputPer1k", "textOutput", "text_output", "outputPrice"})
			}
		}
		if resource == "text" && !hasOutput {
			outputPrice, hasOutput = textPriceFromConfig(nested, []string{"basePrice"})
		}
		if !hasInput {
			inputPrice, hasInput = textPriceFromConfig(nested, []string{"basePrice"})
		}
	}

	if basePrice, ok := numberValue(config["basePrice"]); ok {
		switch strings.ToLower(stringValue(config["resourceType"])) {
		case "text":
			if !hasInput {
				inputPrice, hasInput = basePrice, true
			}
			if !hasOutput {
				outputPrice, hasOutput = basePrice, true
			}
		case "text_total", "text_input":
			if !hasInput {
				inputPrice, hasInput = basePrice, true
			}
		case "text_cached_input":
			if !hasCachedInput {
				cachedInputPrice, hasCachedInput = basePrice, true
			}
		case "text_output":
			if !hasOutput {
				outputPrice, hasOutput = basePrice, true
			}
		}
	}

	lines := []string{}
	if hasInput {
		lines = append(lines, "输入 "+formatNumber(inputPrice)+"/k tokens")
	}
	if hasCachedInput {
		lines = append(lines, "缓存输入 "+formatNumber(cachedInputPrice)+"/k tokens")
	}
	if hasOutput {
		lines = append(lines, "输出 "+formatNumber(outputPrice)+"/k tokens")
	}
	return uniqueStrings(lines)
}

func textPriceFromConfig(config map[string]any, keys []string) (float64, bool) {
	if len(config) == 0 {
		return 0, false
	}
	for _, key := range keys {
		if value, ok := numberValue(config[key]); ok {
			return value, true
		}
	}
	return 0, false
}

func resourceBaseKeys(resource string) []string {
	switch resource {
	case "image":
		return []string{"imageBase"}
	case "image_edit":
		return []string{"editBase", "imageEditBase"}
	case "video":
		return []string{"videoBase"}
	case "audio":
		return []string{"audioBase"}
	case "music":
		return []string{"musicBase"}
	case "digital_human":
		return []string{"digitalHumanBase", "digital_humanBase"}
	case "model":
		return []string{"modelBase"}
	case "text":
		return []string{"textBase"}
	default:
		return []string{resource + "Base"}
	}
}

func priceLine(resourceType string, config map[string]any) string {
	basePrice, _ := numberValue(config["basePrice"])
	weights := resolutionWeightEntries(config["dynamicWeight"])
	if len(weights) > 0 {
		details := make([]string, 0, len(weights))
		for _, item := range weights {
			details = append(details, item.key+" "+formatNumber(basePrice*item.weight))
		}
		line := resourceTypeLabel(resourceType) + "：" + strings.Join(details, " / ")
		if resourceType == "video" {
			line += "（5秒基准）"
		}
		return line
	}
	unit := firstNonEmpty(stringValue(config["unit"]), resourceType)
	if resourceType == "video" && (unit == "video" || unit == "5s") {
		return resourceTypeLabel(resourceType) + "：" + formatNumber(basePrice) + " / 5秒基准"
	}
	return resourceTypeLabel(resourceType) + "：" + formatNumber(basePrice) + " / " + unitLabel(unit)
}

func resourceBaseLine(resourceType string, basePrice float64) string {
	if resourceType == "video" {
		return resourceTypeLabel(resourceType) + "：" + formatNumber(basePrice) + " / 5秒基准"
	}
	return resourceTypeLabel(resourceType) + "：" + formatNumber(basePrice)
}

type resolutionWeight struct {
	key    string
	weight float64
}

func resolutionWeightEntries(value any) []resolutionWeight {
	record := objectValue(value)
	entries := make([]resolutionWeight, 0, len(record))
	for key, raw := range record {
		if !isResolutionKey(key) {
			continue
		}
		weight, ok := numberValue(raw)
		if !ok {
			continue
		}
		entries = append(entries, resolutionWeight{key: key, weight: weight})
	}
	sort.Slice(entries, func(i, j int) bool {
		return resolutionSortValue(entries[i].key) < resolutionSortValue(entries[j].key)
	})
	return entries
}

func isResolutionKey(value string) bool {
	normalized := strings.ToLower(strings.TrimSpace(value))
	if strings.HasSuffix(normalized, "k") {
		_, ok := numberValue(strings.TrimSuffix(normalized, "k"))
		return ok
	}
	if strings.HasSuffix(normalized, "p") {
		_, ok := numberValue(strings.TrimSuffix(normalized, "p"))
		return ok
	}
	return false
}

func resolutionSortValue(value string) float64 {
	normalized := strings.ToLower(strings.TrimSpace(value))
	if strings.HasSuffix(normalized, "k") {
		number, _ := numberValue(strings.TrimSuffix(normalized, "k"))
		return number * 1000
	}
	if strings.HasSuffix(normalized, "p") {
		number, _ := numberValue(strings.TrimSuffix(normalized, "p"))
		return number
	}
	return 0
}

func statusText(sources []catalogSource) string {
	statuses := uniqueStringsFromSources(sources, func(source catalogSource) string {
		if source.platform.Status != "" {
			return source.platform.Status
		}
		if source.model.Enabled {
			return "enabled"
		}
		return "disabled"
	})
	priorities := uniqueStringsFromSources(sources, func(source catalogSource) string {
		if source.platform.Priority == 0 {
			return ""
		}
		return "优先级 " + formatInt(source.platform.Priority)
	})
	parts := append(statuses, priorities...)
	if len(parts) == 0 {
		return "平台模型"
	}
	return strings.Join(parts, " · ")
}

func modelCatalogCapabilityFilters(items []ModelCatalogItem) []ModelCatalogFilterOption {
	counts := map[string]int{}
	for _, item := range items {
		for _, value := range capabilityFilterValuesForItem(item) {
			counts[value]++
		}
	}
	options := []ModelCatalogFilterOption{{Value: "all", Label: "全部", Count: len(items)}}
	known := map[string]bool{}
	for _, option := range modelCatalogCapabilityDefinitions() {
		known[option.Value] = true
		if count := counts[option.Value]; count > 0 {
			option.Count = count
			options = append(options, option)
		}
	}
	extraValues := make([]string, 0)
	for value := range counts {
		if !known[value] {
			extraValues = append(extraValues, value)
		}
	}
	sort.Slice(extraValues, func(i, j int) bool {
		return capabilityLabel(extraValues[i]) < capabilityLabel(extraValues[j])
	})
	for _, value := range extraValues {
		options = append(options, ModelCatalogFilterOption{Value: value, Label: capabilityLabel(value), Count: counts[value]})
	}
	return options
}

func modelCatalogCapabilityDefinitions() []ModelCatalogFilterOption {
	return []ModelCatalogFilterOption{
		{Value: "text_generate", Label: "文本生成"},
		{Value: "chat", Label: "对话"},
		{Value: "responses", Label: "Responses"},
		{Value: "image_generate", Label: "图像生成"},
		{Value: "image_edit", Label: "图像编辑"},
		{Value: "image_analysis", Label: "图像分析"},
		{Value: "video_generate", Label: "视频生成"},
		{Value: "image_to_video", Label: "图生视频"},
		{Value: "text_to_video", Label: "文生视频"},
		{Value: "video_edit", Label: "视频编辑"},
		{Value: "video_understanding", Label: "视频理解"},
		{Value: "audio_generate", Label: "音频生成"},
		{Value: "text_to_speech", Label: "语音合成"},
		{Value: "voice_clone", Label: "音色克隆"},
		{Value: "audio_understanding", Label: "音频理解"},
		{Value: "text_embedding", Label: "Embedding"},
		{Value: "text_rerank", Label: "重排序"},
		{Value: "omni", Label: "全模态"},
		{Value: "omni_video", Label: "全模态视频"},
		{Value: "multimodal", Label: "多模态"},
		{Value: "model_3d", Label: "3D 模型"},
		{Value: "text_to_model", Label: "文生 3D"},
		{Value: "image_to_model", Label: "图生 3D"},
		{Value: "multiview_to_model", Label: "多视角 3D"},
		{Value: "mesh_edit", Label: "3D 模型编辑"},
		{Value: "tools_call", Label: "工具调用"},
		{Value: "structured_output", Label: "结构化输出"},
		{Value: "reasoning", Label: "推理"},
		{Value: "digital_human", Label: "数字人"},
	}
}

func capabilityFilterValuesForItem(item ModelCatalogItem) []string {
	values := capabilityFilterValues(item.ModelType, nil)
	for _, tag := range item.CapabilityTags {
		if value := capabilityFilterValueForTag(tag); value != "" {
			values = append(values, value)
		}
	}
	return uniqueStrings(values)
}

func modelCatalogProviderFilters(items []ModelCatalogItem) []ModelCatalogFilterOption {
	counts := map[string]int{}
	labels := map[string]string{}
	icons := map[string]string{}
	for _, item := range items {
		for _, provider := range item.Providers {
			counts[provider.Key]++
			labels[provider.Key] = provider.Name
			if provider.IconPath != "" {
				icons[provider.Key] = provider.IconPath
			}
		}
	}
	options := []ModelCatalogFilterOption{{Value: "all", Label: "全部", Count: len(items)}}
	keys := sortedIntMapKeys(counts)
	sort.Slice(keys, func(i, j int) bool {
		return labels[keys[i]] < labels[keys[j]]
	})
	for _, key := range keys {
		options = append(options, ModelCatalogFilterOption{
			Value:    key,
			Label:    firstNonEmpty(labels[key], key),
			Count:    counts[key],
			IconPath: icons[key],
		})
	}
	return options
}

func capabilityTagsForGroup(modelTypes []string, capabilities map[string]any) []string {
	tags := make([]string, 0, len(modelTypes)+4)
	for _, modelType := range modelTypes {
		tags = append(tags, capabilityLabel(modelType))
	}
	if boolAnyValue(capabilities["multimodal"]) || boolAnyValue(capabilities["vision"]) {
		tags = append(tags, "多模态")
	}
	if boolAnyValue(capabilities["reasoning"]) {
		tags = append(tags, "推理")
	}
	if boolAnyValue(capabilities["structured_output"]) || boolAnyValue(capabilities["structuredOutput"]) {
		tags = append(tags, "结构化输出")
	}
	if boolAnyValue(capabilities["tools_call"]) || boolAnyValue(capabilities["toolCall"]) || boolAnyValue(capabilities["function_call"]) {
		tags = append(tags, "工具调用")
	}
	return uniqueStrings(tags)
}

func capabilityFilterValues(modelTypes []string, capabilities map[string]any) []string {
	values := []string{}
	for _, modelType := range modelTypes {
		if value := canonicalCapabilityFilterValue(modelType); value != "" {
			values = append(values, value)
		}
	}
	if boolAnyValue(capabilities["tools_call"]) || boolAnyValue(capabilities["toolCall"]) || boolAnyValue(capabilities["function_call"]) {
		values = append(values, "tools_call")
	}
	if boolAnyValue(capabilities["multimodal"]) {
		values = append(values, "multimodal")
	}
	return uniqueStrings(values)
}

func canonicalCapabilityFilterValue(value string) string {
	normalized := strings.ReplaceAll(strings.ToLower(strings.TrimSpace(value)), "-", "_")
	switch normalized {
	case "embedding":
		return "text_embedding"
	case "rerank", "reranks":
		return "text_rerank"
	case "vectorize", "image_vectorizer":
		return "image_vectorize"
	case "video_upscale", "upscale":
		return "video_enhance"
	case "model":
		return "model_3d"
	default:
		return normalized
	}
}

func capabilityFilterValueForTag(tag string) string {
	switch tag {
	case "工具调用":
		return "tools_call"
	case "全模态":
		return "omni"
	case "全模态视频":
		return "omni_video"
	case "多模态":
		return "multimodal"
	case "推理":
		return "reasoning"
	case "结构化输出":
		return "structured_output"
	case "数字人":
		return "digital_human"
	case "图片矢量化":
		return "image_vectorize"
	case "视频增强":
		return "video_enhance"
	case "重排序":
		return "text_rerank"
	case "3D 模型":
		return "model_3d"
	case "文生 3D":
		return "text_to_model"
	case "图生 3D":
		return "image_to_model"
	case "多视角 3D":
		return "multiview_to_model"
	case "3D 模型编辑":
		return "mesh_edit"
	default:
		return ""
	}
}

func capabilityLabel(value string) string {
	labels := map[string]string{
		"text_generate":       "文本生成",
		"chat":                "对话",
		"responses":           "Responses",
		"text_embedding":      "Embedding",
		"embedding":           "Embedding",
		"text_rerank":         "重排序",
		"rerank":              "重排序",
		"reranks":             "重排序",
		"image_generate":      "图像生成",
		"image_edit":          "图像编辑",
		"image_analysis":      "图像分析",
		"image_vectorize":     "图片矢量化",
		"video_generate":      "视频生成",
		"image_to_video":      "图生视频",
		"text_to_video":       "文生视频",
		"video_edit":          "视频编辑",
		"video_understanding": "视频理解",
		"video_enhance":       "视频增强",
		"audio_generate":      "音频生成",
		"text_to_speech":      "语音合成",
		"voice_clone":         "音色克隆",
		"audio_understanding": "音频理解",
		"tools_call":          "工具调用",
		"omni":                "全模态",
		"omni_video":          "全模态视频",
		"multimodal":          "多模态",
		"reasoning":           "推理",
		"structured_output":   "结构化输出",
		"digital_human":       "数字人",
		"model":               "3D 模型",
		"model_3d":            "3D 模型",
		"text_to_model":       "文生 3D",
		"image_to_model":      "图生 3D",
		"multiview_to_model":  "多视角 3D",
		"mesh_edit":           "3D 模型编辑",
	}
	if label, ok := labels[value]; ok {
		return label
	}
	return value
}

func modelDescription(model store.PlatformModel, baseModel store.BaseModel) string {
	rawModel := objectValue(baseModel.Metadata["rawModel"])
	capabilityRawModel := objectValue(model.Capabilities["rawModel"])
	return firstNonEmpty(
		stringValue(baseModel.Metadata["description"]),
		stringValue(baseModel.Metadata["modelDescription"]),
		stringValue(rawModel["description"]),
		stringValue(model.Capabilities["description"]),
		stringValue(model.Capabilities["modelDescription"]),
		stringValue(capabilityRawModel["description"]),
	)
}

func modelIconPath(model store.PlatformModel, baseModel store.BaseModel, provider store.CatalogProvider) string {
	rawModel := objectValue(baseModel.Metadata["rawModel"])
	return firstNonEmpty(
		stringValue(baseModel.Metadata["iconPath"]),
		stringValue(rawModel["icon_path"]),
		stringValue(rawModel["iconPath"]),
		stringValue(model.Capabilities["iconPath"]),
		stringValue(model.Capabilities["icon_path"]),
		stringValue(model.BillingConfig["iconPath"]),
		stringValue(model.BillingConfig["icon_path"]),
		provider.IconPath,
	)
}

func catalogIdentityKey(model store.PlatformModel, baseModel store.BaseModel) string {
	key := strings.ToLower(strings.TrimSpace(firstNonEmpty(baseModel.InvocationName, baseModel.CanonicalModelKey, model.ModelName)))
	if key == "" {
		return model.ID
	}
	return key
}

func modelProviderKey(model store.PlatformModel, baseModel store.BaseModel, platform store.Platform) string {
	return normalizeCatalogProviderKey(firstNonEmpty(
		baseModel.ProviderKey,
		stringValue(baseModel.Metadata["sourceProviderCode"]),
		stringValue(baseModel.Metadata["providerKey"]),
		model.Provider,
		platform.Provider,
		platform.Name,
		model.PlatformName,
	))
}

func normalizeCatalogProviderKey(value string) string {
	normalized := strings.ToLower(strings.TrimSpace(value))
	if normalized == "" {
		return "unknown"
	}
	normalized = strings.ReplaceAll(normalized, "_", "-")
	normalized = strings.Join(strings.Fields(normalized), "-")
	switch normalized {
	case "google-gemini":
		return "gemini"
	case "open-ai":
		return "openai"
	}
	return normalized
}

func mapPlatforms(platforms []store.Platform) map[string]store.Platform {
	out := map[string]store.Platform{}
	for _, platform := range platforms {
		out[platform.ID] = platform
	}
	return out
}

func mapProviders(providers []store.CatalogProvider) map[string]store.CatalogProvider {
	out := map[string]store.CatalogProvider{}
	for _, provider := range providers {
		keys := []string{
			provider.ProviderKey,
			provider.Code,
			provider.DisplayName,
			stringValue(provider.Metadata["sourceCode"]),
		}
		for _, key := range keys {
			normalized := normalizeCatalogProviderKey(key)
			if normalized != "unknown" {
				out[normalized] = provider
			}
		}
	}
	return out
}

func mapRuntimePolicies(policies []store.RuntimePolicySet) map[string]store.RuntimePolicySet {
	out := map[string]store.RuntimePolicySet{}
	for _, policy := range policies {
		out[policy.ID] = policy
	}
	return out
}

func mapUserGroups(groups []store.UserGroup) map[string]string {
	out := map[string]string{}
	for _, group := range groups {
		out[group.ID] = firstNonEmpty(group.Name, group.GroupKey, group.ID)
	}
	return out
}

func mapBaseModels(models []store.BaseModel) map[string]store.BaseModel {
	out := map[string]store.BaseModel{}
	for _, model := range models {
		out[model.ID] = model
	}
	return out
}

func mergeCatalogObjects(left map[string]any, right map[string]any) map[string]any {
	if len(left) == 0 && len(right) == 0 {
		return nil
	}
	out := cloneObject(left)
	for key, value := range right {
		out[key] = value
	}
	return out
}

func cloneObject(value map[string]any) map[string]any {
	if len(value) == 0 {
		return nil
	}
	out := make(map[string]any, len(value))
	for key, item := range value {
		out[key] = item
	}
	return out
}

func cloneStringSlice(values []string) []string {
	if len(values) == 0 {
		return nil
	}
	return append([]string(nil), values...)
}

func uniqueStrings(values []string) []string {
	out := make([]string, 0, len(values))
	seen := map[string]bool{}
	for _, value := range values {
		trimmed := strings.TrimSpace(value)
		if trimmed == "" || seen[trimmed] {
			continue
		}
		seen[trimmed] = true
		out = append(out, trimmed)
	}
	return out
}

func uniqueStringsFromSources(sources []catalogSource, selector func(catalogSource) string) []string {
	values := make([]string, 0, len(sources))
	for _, source := range sources {
		values = append(values, selector(source))
	}
	return uniqueStrings(values)
}

func limitStrings(values []string, limit int) []string {
	if len(values) <= limit {
		return values
	}
	return values[:limit]
}

func sortedMapKeys(values map[string]bool) []string {
	keys := make([]string, 0, len(values))
	for key := range values {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	return keys
}

func objectValue(value any) map[string]any {
	switch typed := value.(type) {
	case map[string]any:
		return typed
	default:
		return nil
	}
}

func stringValue(value any) string {
	if value == nil {
		return ""
	}
	if text, ok := value.(string); ok {
		return strings.TrimSpace(text)
	}
	return ""
}

func stringSliceValue(value any) []string {
	switch typed := value.(type) {
	case []string:
		return uniqueStrings(typed)
	case []any:
		out := make([]string, 0, len(typed))
		for _, item := range typed {
			if text := stringValue(item); text != "" {
				out = append(out, text)
			}
		}
		return uniqueStrings(out)
	default:
		return nil
	}
}

func sortedIntMapKeys(values map[string]int) []string {
	keys := make([]string, 0, len(values))
	for key := range values {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	return keys
}

func boolAnyValue(value any) bool {
	typed, ok := value.(bool)
	return ok && typed
}

func numberValue(value any) (float64, bool) {
	switch typed := value.(type) {
	case float64:
		return typed, true
	case float32:
		return float64(typed), true
	case int:
		return float64(typed), true
	case int64:
		return float64(typed), true
	case int32:
		return float64(typed), true
	case string:
		trimmed := strings.TrimSpace(typed)
		if trimmed == "" {
			return 0, false
		}
		if number, err := strconv.ParseFloat(trimmed, 64); err == nil {
			return number, true
		}
		return 0, false
	default:
		return 0, false
	}
}

func formatOptionalNumber(value *float64) string {
	if value == nil || *value <= 0 {
		return "不限"
	}
	return formatLimitNumber(*value)
}

func floatPointer(value float64) *float64 {
	return &value
}

func formatLimitNumber(value float64) string {
	switch {
	case absFloat(value) >= 10000:
		return formatNumber(value/10000) + "万"
	case absFloat(value) >= 1000:
		return formatNumber(value/1000) + "k"
	default:
		return formatNumber(value)
	}
}

func formatPercent(value float64) string {
	return formatNumber(value*100) + "%"
}

func formatNumber(value float64) string {
	return strconv.FormatFloat(value, 'f', -1, 64)
}

func formatInt(value int) string {
	return strconv.Itoa(value)
}

func absFloat(value float64) float64 {
	if value < 0 {
		return -value
	}
	return value
}

func pricingModeLabel(mode string) string {
	switch mode {
	case "inherit":
		return "跟随基准定价"
	case "inherit_discount":
		return "继承并折扣"
	case "custom":
		return "自定义定价"
	default:
		if strings.TrimSpace(mode) != "" {
			return mode
		}
		return "未配置定价"
	}
}

func resourceTypeLabel(value string) string {
	labels := map[string]string{
		"text":          "文本",
		"text_total":    "文本",
		"text_input":    "文本输入",
		"text_output":   "文本输出",
		"image":         "图像",
		"image_edit":    "图像编辑",
		"video":         "视频",
		"audio":         "音频",
		"music":         "音乐",
		"digital_human": "数字人",
		"model":         "模型",
	}
	if label, ok := labels[value]; ok {
		return label
	}
	return value
}

func unitLabel(value string) string {
	labels := map[string]string{
		"image":        "张",
		"5s":           "5秒",
		"second":       "秒",
		"item":         "次",
		"unit":         "单位",
		"1k_tokens":    "k tokens",
		"character_1k": "千字符",
	}
	if label, ok := labels[value]; ok {
		return label
	}
	return value
}
