package workspace

import (
	"slices"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/store/organization"
)

func selectableBYOKModelOptions(providers []organization.OrganizationProviderView) []dto.SelectableModelOption {
	options := make([]dto.SelectableModelOption, 0)
	for _, provider := range providers {
		providerID := strings.TrimSpace(provider.ID)
		providerName := strings.TrimSpace(provider.Name)
		for _, model := range provider.Models {
			modelRef := strings.TrimSpace(model.ID)
			if modelRef == "" {
				continue
			}
			displayName := strings.TrimSpace(model.Name)
			if displayName == "" {
				displayName = modelRef
			}
			options = append(options, dto.SelectableModelOption{
				Source:         selectableSourceBYOK,
				ModelReference: modelRef,
				DisplayName:    displayName,
				Enabled:        true,
				Actions:        []string{selectableActionSelect},
				Provider: &dto.SelectableModelProvider{
					ID:         providerID,
					Name:       providerName,
					DriverKind: strings.TrimSpace(provider.DriverKind),
				},
			})
		}
	}
	return options
}

func selectableHostedModelOptions(availability HostedAvailability, selection organization.OrganizationHostedModelSelectionView, query selectableModelQuery, pricing HostedPricing) []dto.SelectableModelOption {
	options := make([]dto.SelectableModelOption, 0, len(availability.Models))
	prices := hostedPricingByModelReference(pricing)
	for _, model := range availability.Models {
		modelRef := strings.TrimSpace(model.ModelReference)
		if modelRef == "" {
			continue
		}

		reasons := normalizeSelectableReasons(model.Reasons)
		enabled := model.Enabled
		if !hostedAuthorizationCoversSelection(selection, modelRef) {
			enabled = false
			reasons = appendReason(reasons, hostedReasonWorkspaceNotAuthorized)
		}
		if !hostedModelSupportsFeatures(model.MessagesFeatures, query.Features) {
			enabled = false
			reasons = appendReason(reasons, hostedReasonFeatureUnsupported)
		}
		if !enabled && strings.TrimSpace(selection.PickerVisibilityPolicy) == hostedPickerVisibilityHideDisabled {
			continue
		}

		displayName := strings.TrimSpace(model.DisplayName)
		if displayName == "" {
			displayName = modelRef
		}
		price, hasPrice := prices[modelRef]
		price = selectableHostedModelPrice(model, price, hasPrice)
		options = append(options, dto.SelectableModelOption{
			Source:                    selectableSourceHosted,
			ModelReference:            modelRef,
			DisplayName:               displayName,
			Enabled:                   enabled,
			Reasons:                   selectableReasonsForCaller(reasons, query.IsAdmin),
			Actions:                   selectableHostedActions(enabled, reasons, query.IsAdmin),
			RequiresMachineCapability: hostedMachineCapabilityMaaSDirect,
			Hosted: &dto.SelectableHostedModelMetadata{
				HostedModelID:                                 strings.TrimSpace(model.HostedModelID),
				InventoryStatus:                               strings.TrimSpace(model.InventoryStatus),
				EntitlementStatus:                             strings.TrimSpace(model.EntitlementStatus),
				AccessSource:                                  strings.TrimSpace(model.AccessSource),
				BalanceStatus:                                 strings.TrimSpace(model.BalanceStatus),
				RuntimeStatus:                                 strings.TrimSpace(model.RuntimeStatus),
				PricingVersionID:                              strings.TrimSpace(price.PricingVersionID),
				EffectivePricingVersionID:                     strings.TrimSpace(price.EffectivePricingVersionID),
				PricingFormulaVersion:                         strings.TrimSpace(price.PricingFormulaVersion),
				BillableDimensions:                            normalizeSelectableReasons(price.BillableDimensions),
				ContextTiers:                                  selectableHostedPricingTiers(price.ContextTiers),
				BasePricingVersionID:                          strings.TrimSpace(price.BasePricingVersionID),
				BasePricingFormulaVersion:                     strings.TrimSpace(price.BasePricingFormulaVersion),
				BaseBillableDimensions:                        normalizeSelectableReasons(price.BaseBillableDimensions),
				BaseContextTiers:                              selectableHostedPricingTiers(price.BaseContextTiers),
				SupportedFamilies:                             normalizeSelectableReasons(model.SupportedFamilies),
				ExecutableWireAPIFamilies:                     normalizeSelectableReasons(model.ExecutableWireAPIFamilies),
				MessagesFeatures:                              normalizeSelectableReasons(model.MessagesFeatures),
				ChatCompletionsFeatures:                       normalizeSelectableReasons(model.ChatCompletionsFeatures),
				ResponsesFeatures:                             normalizeSelectableReasons(model.ResponsesFeatures),
				InputCreditAtomsPerMillionTokens:              price.InputCreditAtomsPerMillionTokens,
				CacheReadInputCreditAtomsPerMillionTokens:     price.CacheReadInputCreditAtomsPerMillionTokens,
				OutputCreditAtomsPerMillionTokens:             price.OutputCreditAtomsPerMillionTokens,
				BaseInputCreditAtomsPerMillionTokens:          price.BaseInputCreditAtomsPerMillionTokens,
				BaseCacheReadInputCreditAtomsPerMillionTokens: price.BaseCacheReadInputCreditAtomsPerMillionTokens,
				BaseOutputCreditAtomsPerMillionTokens:         price.BaseOutputCreditAtomsPerMillionTokens,
				PricingOverride:                               selectableHostedPricingOverride(price.PricingOverride),
				UpdatedAt:                                     availability.UpdatedAt,
			},
		})
	}
	return options
}

func hostedPricingByModelReference(pricing HostedPricing) map[string]HostedModelPricing {
	prices := make(map[string]HostedModelPricing, len(pricing.HostedModels))
	for _, model := range pricing.HostedModels {
		modelRef := strings.TrimSpace(model.ModelReference)
		if modelRef == "" {
			continue
		}
		prices[modelRef] = model
	}
	return prices
}

func selectableHostedModelPrice(model HostedAvailabilityModel, pricing HostedModelPricing, hasPricing bool) HostedModelPricing {
	if hasPricing {
		return pricing
	}
	return HostedModelPricing{
		ModelReference:                            model.ModelReference,
		HostedModelID:                             model.HostedModelID,
		PricingVersionID:                          "",
		EffectivePricingVersionID:                 "",
		PricingFormulaVersion:                     model.PricingFormulaVersion,
		BillableDimensions:                        append([]string(nil), model.BillableDimensions...),
		ContextTiers:                              selectableAvailabilityPricingTiers(model.ContextTiers),
		InputCreditAtomsPerMillionTokens:          model.InputCreditAtomsPerMillionTokens,
		CacheReadInputCreditAtomsPerMillionTokens: model.CacheReadInputCreditAtomsPerMillionTokens,
		OutputCreditAtomsPerMillionTokens:         model.OutputCreditAtomsPerMillionTokens,
	}
}

func selectableHostedPricingOverride(override *HostedPricingOverride) *dto.HostedPricingOverrideResponse {
	if override == nil {
		return nil
	}
	return &dto.HostedPricingOverrideResponse{
		ID:                        strings.TrimSpace(override.ID),
		HostedModelID:             strings.TrimSpace(override.HostedModelID),
		ScopeType:                 strings.TrimSpace(override.ScopeType),
		ScopeID:                   strings.TrimSpace(override.ScopeID),
		BasePricingVersionID:      strings.TrimSpace(override.BasePricingVersionID),
		EffectivePricingVersionID: strings.TrimSpace(override.EffectivePricingVersionID),
		OverrideType:              strings.TrimSpace(override.OverrideType),
		DisplayLabel:              strings.TrimSpace(override.DisplayLabel),
		Priority:                  override.Priority,
		EffectiveFrom:             override.EffectiveFrom,
		EffectiveTo:               override.EffectiveTo,
		Status:                    strings.TrimSpace(override.Status),
	}
}

func selectableHostedPricingTiers(tiers []HostedPricingTier) []dto.HostedModelPricingTierResponse {
	if len(tiers) == 0 {
		return nil
	}
	presented := make([]dto.HostedModelPricingTierResponse, 0, len(tiers))
	for _, tier := range tiers {
		presented = append(presented, dto.HostedModelPricingTierResponse{
			ID:               strings.TrimSpace(tier.ID),
			MinContextTokens: tier.MinContextTokens,
			MaxContextTokens: tier.MaxContextTokens,
			Rates: dto.HostedModelPricingTierRatesResponse{
				InputTokensCreditAtomsPerMillionTokens:    tier.Rates.InputTokensCreditAtomsPerMillionTokens,
				CacheReadInputCreditAtomsPerMillionTokens: tier.Rates.CacheReadInputCreditAtomsPerMillionTokens,
				OutputCreditAtomsPerMillionTokens:         tier.Rates.OutputCreditAtomsPerMillionTokens,
			},
		})
	}
	return presented
}

func selectableAvailabilityPricingTiers(tiers []HostedAvailabilityPricingTier) []HostedPricingTier {
	if len(tiers) == 0 {
		return nil
	}
	copied := make([]HostedPricingTier, 0, len(tiers))
	for _, tier := range tiers {
		copied = append(copied, HostedPricingTier{
			ID:               strings.TrimSpace(tier.ID),
			MinContextTokens: tier.MinContextTokens,
			MaxContextTokens: tier.MaxContextTokens,
			Rates: HostedPricingTierRates{
				InputTokensCreditAtomsPerMillionTokens:    tier.Rates.InputTokensCreditAtomsPerMillionTokens,
				CacheReadInputCreditAtomsPerMillionTokens: tier.Rates.CacheReadInputCreditAtomsPerMillionTokens,
				OutputCreditAtomsPerMillionTokens:         tier.Rates.OutputCreditAtomsPerMillionTokens,
			},
		})
	}
	return copied
}

func hostedAuthorizationCoversSelection(selection organization.OrganizationHostedModelSelectionView, modelRef string) bool {
	switch strings.TrimSpace(selection.AuthorizationMode) {
	case hostedAuthorizationAllActive:
		return true
	case hostedAuthorizationSelected:
		return slices.Contains(normalizeSelectableReasons(selection.SelectedModelRefs), strings.TrimSpace(modelRef))
	default:
		return false
	}
}

func hostedModelSupportsFeatures(supported []string, requested []string) bool {
	if len(requested) == 0 {
		return true
	}
	normalizedSupported := normalizeSelectableReasons(supported)
	for _, feature := range normalizeSelectableReasons(requested) {
		if !slices.Contains(normalizedSupported, feature) {
			return false
		}
	}
	return true
}

func normalizeSelectableReasons(items []string) []string {
	normalized := make([]string, 0, len(items))
	seen := make(map[string]struct{}, len(items))
	for _, item := range items {
		trimmed := strings.TrimSpace(item)
		if trimmed == "" {
			continue
		}
		if _, ok := seen[trimmed]; ok {
			continue
		}
		seen[trimmed] = struct{}{}
		normalized = append(normalized, trimmed)
	}
	return normalized
}

func appendReason(reasons []string, reason string) []string {
	trimmed := strings.TrimSpace(reason)
	if trimmed == "" || slices.Contains(reasons, trimmed) {
		return reasons
	}
	return append(reasons, trimmed)
}

func selectableReasonsForCaller(reasons []string, isAdmin bool) []string {
	reasons = normalizeSelectableReasons(reasons)
	if isAdmin || len(reasons) == 0 {
		return reasons
	}
	safeReasons := make([]string, 0, len(reasons))
	for _, reason := range reasons {
		switch reason {
		case hostedReasonFeatureUnsupported:
			safeReasons = appendReason(safeReasons, hostedReasonFeatureUnsupported)
		default:
			safeReasons = appendReason(safeReasons, hostedReasonAdminActionRequired)
		}
	}
	return safeReasons
}

func selectableHostedActions(enabled bool, reasons []string, isAdmin bool) []string {
	if enabled {
		return []string{selectableActionSelect}
	}
	if !isAdmin {
		return []string{selectableActionContactAdmin}
	}
	if slices.Contains(reasons, hostedReasonWorkspaceNotAuthorized) {
		return []string{selectableActionManageWorkspaceAuthorization}
	}
	if slices.Contains(reasons, "zero_balance") {
		return []string{selectableActionRechargeCredits}
	}
	return []string{selectableActionManageHostedModels}
}
