package runner

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"math/big"
	"strconv"
	"strings"

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

const (
	pricingVersionV2 = "effective-pricing-v2"
	fixedScale       = int64(1_000_000_000)
	maxFixedAmount   = fixedAmount(math.MaxInt64)
)

var ErrPricingUnavailable = errors.New("pricing unavailable")
var errFixedAmountOverflow = errors.New("fixed amount overflow")

type PricingUnavailableError struct {
	Reason       string
	ResourceType string
	RuleSetID    string
}

func (e *PricingUnavailableError) Error() string {
	message := "pricing unavailable"
	if e.ResourceType != "" {
		message += " for " + e.ResourceType
	}
	if e.Reason != "" {
		message += ": " + e.Reason
	}
	return message
}

func (e *PricingUnavailableError) Unwrap() error { return ErrPricingUnavailable }

func isPricingUnavailable(err error) bool { return errors.Is(err, ErrPricingUnavailable) }

func IsPricingUnavailable(err error) bool { return errors.Is(err, ErrPricingUnavailable) }

func PricingUnavailableDetails(err error) map[string]any {
	var pricingErr *PricingUnavailableError
	if !errors.As(err, &pricingErr) {
		return nil
	}
	details := map[string]any{"reason": pricingErr.Reason}
	if pricingErr.ResourceType != "" {
		details["resourceType"] = pricingErr.ResourceType
	}
	if pricingErr.RuleSetID != "" {
		details["ruleSetId"] = pricingErr.RuleSetID
	}
	return details
}

// fixedAmount is a signed decimal with exactly nine fractional digits.
// Monetary decisions use this type; float64 is only produced at JSON-compatible boundaries.
type fixedAmount int64

func parseFixedAmount(value string) (fixedAmount, error) {
	value = strings.TrimSpace(value)
	if value == "" {
		return 0, fmt.Errorf("empty decimal")
	}
	sign := int64(1)
	if value[0] == '-' || value[0] == '+' {
		if value[0] == '-' {
			sign = -1
		}
		value = value[1:]
	}
	parts := strings.Split(value, ".")
	if len(parts) > 2 || value == "" {
		return 0, fmt.Errorf("invalid decimal %q", value)
	}
	whole := parts[0]
	if whole == "" {
		whole = "0"
	}
	fraction := ""
	if len(parts) == 2 {
		fraction = parts[1]
	}
	for _, digits := range []string{whole, fraction} {
		for _, char := range digits {
			if char < '0' || char > '9' {
				return 0, fmt.Errorf("invalid decimal %q", value)
			}
		}
	}
	roundUp := false
	if len(fraction) > 9 {
		roundUp = fraction[9] >= '5'
		fraction = fraction[:9]
	}
	fraction += strings.Repeat("0", 9-len(fraction))
	combined := strings.TrimLeft(whole+fraction, "0")
	if combined == "" {
		combined = "0"
	}
	number := new(big.Int)
	if _, ok := number.SetString(combined, 10); !ok {
		return 0, fmt.Errorf("invalid decimal %q", value)
	}
	if roundUp {
		number.Add(number, big.NewInt(1))
	}
	if !number.IsInt64() {
		return 0, fmt.Errorf("decimal %q exceeds fixed amount range", value)
	}
	return fixedAmount(sign * number.Int64()), nil
}

func fixedAmountFromAny(value any) (fixedAmount, error) {
	switch typed := value.(type) {
	case fixedAmount:
		return typed, nil
	case string:
		return parseFixedAmount(typed)
	case json.Number:
		return parseFixedAmount(typed.String())
	case int:
		return parseFixedAmount(strconv.FormatInt(int64(typed), 10))
	case int64:
		return parseFixedAmount(strconv.FormatInt(typed, 10))
	case int32:
		return parseFixedAmount(strconv.FormatInt(int64(typed), 10))
	case float64:
		if math.IsNaN(typed) || math.IsInf(typed, 0) {
			return 0, fmt.Errorf("non-finite decimal")
		}
		return parseFixedAmount(strconv.FormatFloat(typed, 'f', 9, 64))
	case float32:
		return fixedAmountFromAny(float64(typed))
	default:
		return 0, fmt.Errorf("unsupported decimal type %T", value)
	}
}

func (a fixedAmount) String() string {
	value := int64(a)
	sign := ""
	if value < 0 {
		sign = "-"
		value = -value
	}
	return fmt.Sprintf("%s%d.%09d", sign, value/fixedScale, value%fixedScale)
}

func (a fixedAmount) Float64() float64 { return float64(a) / float64(fixedScale) }
func (a fixedAmount) IsZero() bool     { return a == 0 }

func addFixedAmounts(left fixedAmount, right fixedAmount) (fixedAmount, error) {
	value := new(big.Int).Add(big.NewInt(int64(left)), big.NewInt(int64(right)))
	return fixedAmountFromBigInt(value)
}

func multiplyFixedAmountByInt(amount fixedAmount, multiplier int) (fixedAmount, error) {
	value := new(big.Int).Mul(big.NewInt(int64(amount)), big.NewInt(int64(multiplier)))
	return fixedAmountFromBigInt(value)
}

func multiplyFixedAmounts(left fixedAmount, right fixedAmount) (fixedAmount, error) {
	product := new(big.Int).Mul(big.NewInt(int64(left)), big.NewInt(int64(right)))
	return fixedAmountFromBigInt(roundBigIntRatio(product, big.NewInt(fixedScale)))
}

func multiplyFixedAmountRatio(amount fixedAmount, numerator int, denominator int) (fixedAmount, error) {
	if denominator == 0 {
		return 0, fmt.Errorf("division by zero")
	}
	product := new(big.Int).Mul(big.NewInt(int64(amount)), big.NewInt(int64(numerator)))
	return fixedAmountFromBigInt(roundBigIntRatio(product, big.NewInt(int64(denominator))))
}

func multiplyFixedProductRatio(base fixedAmount, integerFactors []int, fixedFactors []fixedAmount, denominator int) (fixedAmount, error) {
	if denominator == 0 {
		return 0, fmt.Errorf("division by zero")
	}
	product := big.NewInt(int64(base))
	for _, factor := range integerFactors {
		product.Mul(product, big.NewInt(int64(factor)))
	}
	divisor := big.NewInt(int64(denominator))
	for _, factor := range fixedFactors {
		product.Mul(product, big.NewInt(int64(factor)))
		divisor.Mul(divisor, big.NewInt(fixedScale))
	}
	return fixedAmountFromBigInt(roundBigIntRatio(product, divisor))
}

func fixedAmountFromBigInt(value *big.Int) (fixedAmount, error) {
	if value == nil || !value.IsInt64() {
		return 0, errFixedAmountOverflow
	}
	return fixedAmount(value.Int64()), nil
}

func roundBigIntRatio(numerator *big.Int, denominator *big.Int) *big.Int {
	negative := numerator.Sign() < 0
	absolute := new(big.Int).Abs(new(big.Int).Set(numerator))
	quotient, remainder := new(big.Int), new(big.Int)
	quotient.QuoRem(absolute, denominator, remainder)
	if new(big.Int).Mul(remainder, big.NewInt(2)).Cmp(denominator) >= 0 {
		quotient.Add(quotient, big.NewInt(1))
	}
	if negative {
		quotient.Neg(quotient)
	}
	return quotient
}

type resolvedPricing struct {
	Config       map[string]any
	Currency     string
	RuleSetID    string
	RuleSetKey   string
	Source       string
	FreeResource map[string]bool
	Snapshot     map[string]any
}

func (pricing resolvedPricing) requiredPrice(resource string, keys ...string) (fixedAmount, error) {
	if price, found, err := pricing.price(resource, keys...); err != nil {
		return 0, &PricingUnavailableError{Reason: err.Error(), ResourceType: resource, RuleSetID: pricing.RuleSetID}
	} else if found && price > 0 {
		return price, nil
	} else if found && price == 0 && pricing.FreeResource[resource] {
		return 0, nil
	}
	return 0, &PricingUnavailableError{Reason: "missing, invalid, or not explicitly free", ResourceType: resource, RuleSetID: pricing.RuleSetID}
}

func (pricing resolvedPricing) price(resource string, keys ...string) (fixedAmount, bool, error) {
	for _, key := range keys {
		if value, ok := pricing.Config[key]; ok {
			amount, err := fixedAmountFromAny(value)
			return amount, true, err
		}
	}
	resourceConfig, _ := pricing.Config[resource].(map[string]any)
	if len(resourceConfig) == 0 && resource == "image_edit" {
		resourceConfig, _ = pricing.Config["image"].(map[string]any)
	}
	for _, key := range keys {
		if value, ok := resourceConfig[key]; ok {
			amount, err := fixedAmountFromAny(value)
			return amount, true, err
		}
	}
	if formula, ok := resourceConfig["formulaConfig"].(map[string]any); ok {
		for _, key := range keys {
			if value, exists := formula[key]; exists {
				amount, err := fixedAmountFromAny(value)
				return amount, true, err
			}
		}
	}
	if value, ok := resourceConfig["basePrice"]; ok {
		amount, err := fixedAmountFromAny(value)
		return amount, true, err
	}
	if resource == "text" {
		return resolvedPricing{Config: pricing.Config}.price("text_total", keys...)
	}
	return 0, false, nil
}

func (pricing resolvedPricing) weight(resource string, key string, name string) (fixedAmount, error) {
	if strings.TrimSpace(name) == "" {
		return fixedAmount(fixedScale), nil
	}
	keys := weightKeyAliases(key)
	names := weightValueAliases(key, name)
	value, found := pricingWeightValue(pricing.Config, resource, keys, names)
	if !found {
		return fixedAmount(fixedScale), nil
	}
	weight, err := fixedAmountFromAny(value)
	if err != nil || weight <= 0 {
		reason := fmt.Sprintf("invalid %s weight for %s", key, name)
		if err != nil {
			reason += ": " + err.Error()
		}
		return 0, &PricingUnavailableError{Reason: reason, ResourceType: resource, RuleSetID: pricing.RuleSetID}
	}
	return weight, nil
}

func (pricing resolvedPricing) calculate(resource string, base fixedAmount, integerFactors []int, fixedFactors ...fixedAmount) (fixedAmount, error) {
	amount := base
	var err error
	for _, factor := range integerFactors {
		amount, err = multiplyFixedAmountByInt(amount, factor)
		if err != nil {
			return 0, pricing.calculationError(resource, err)
		}
	}
	for _, factor := range fixedFactors {
		amount, err = multiplyFixedAmounts(amount, factor)
		if err != nil {
			return 0, pricing.calculationError(resource, err)
		}
	}
	return amount, nil
}

func (pricing resolvedPricing) calculateRatio(resource string, base fixedAmount, numerator int, denominator int, fixedFactors ...fixedAmount) (fixedAmount, error) {
	amount, err := multiplyFixedAmountRatio(base, numerator, denominator)
	if err != nil {
		return 0, pricing.calculationError(resource, err)
	}
	return pricing.calculate(resource, amount, nil, fixedFactors...)
}

func (pricing resolvedPricing) add(resource string, left fixedAmount, right fixedAmount) (fixedAmount, error) {
	amount, err := addFixedAmounts(left, right)
	if err != nil {
		return 0, pricing.calculationError(resource, err)
	}
	return amount, nil
}

func (pricing resolvedPricing) calculationError(resource string, err error) error {
	return &PricingUnavailableError{Reason: "pricing calculation failed: " + err.Error(), ResourceType: resource, RuleSetID: pricing.RuleSetID}
}

func pricingWeightValue(config map[string]any, resource string, keys []string, names []string) (any, bool) {
	if value, ok := pricingWeightValueFromConfig(config, keys, names); ok {
		return value, true
	}
	resourceConfig, _ := config[resource].(map[string]any)
	if len(resourceConfig) == 0 && resource == "image_edit" {
		resourceConfig, _ = config["image"].(map[string]any)
	}
	return pricingWeightValueFromConfig(resourceConfig, keys, names)
}

func pricingWeightValueFromConfig(config map[string]any, keys []string, names []string) (any, bool) {
	if len(config) == 0 {
		return nil, false
	}
	for _, key := range keys {
		weights, _ := config[key].(map[string]any)
		for _, name := range names {
			if value, ok := weights[name]; ok {
				return value, true
			}
		}
	}
	dynamic, _ := config["dynamicWeight"].(map[string]any)
	for _, name := range names {
		if value, ok := dynamic[name]; ok {
			return value, true
		}
	}
	for _, key := range keys {
		weights, _ := dynamic[key].(map[string]any)
		for _, name := range names {
			if value, ok := weights[name]; ok {
				return value, true
			}
		}
	}
	return nil, false
}

type candidateEstimate struct {
	Items    []any
	Amount   fixedAmount
	Currency string
	Snapshot map[string]any
	Pricing  resolvedPricing
}

func buildEstimateResult(estimates []candidateEstimate, fingerprint string) (EstimateResult, error) {
	if len(estimates) == 0 {
		return EstimateResult{}, &PricingUnavailableError{Reason: "no candidate has effective pricing"}
	}
	preferred := estimates[0]
	reservation := preferred.Amount
	currency := preferred.Currency
	if currency == "" {
		currency = "resource"
	}
	for _, estimate := range estimates[1:] {
		candidateCurrency := estimate.Currency
		if candidateCurrency == "" {
			candidateCurrency = "resource"
		}
		if candidateCurrency != currency {
			return EstimateResult{}, &PricingUnavailableError{Reason: "candidate currencies do not match"}
		}
		if estimate.Amount > reservation {
			reservation = estimate.Amount
		}
	}
	return EstimateResult{
		Items:              preferred.Items,
		Resolver:           pricingVersionV2,
		TotalAmount:        preferred.Amount.Float64(),
		ReservationAmount:  reservation.Float64(),
		Currency:           currency,
		CandidateCount:     len(estimates),
		PricingVersion:     pricingVersionV2,
		RequestFingerprint: fingerprint,
	}, nil
}

func estimatedOutputTokens(body map[string]any, candidate store.RuntimeModelCandidate) int {
	for _, key := range []string{"max_completion_tokens", "max_output_tokens", "max_tokens"} {
		if value, ok := positiveInteger(body[key]); ok {
			return value
		}
	}
	if limit, _, _, ok := candidateMaxOutputTokens(candidate, candidate.ModelType); ok {
		return limit
	}
	return 4096
}

func pricingRequestFingerprint(kind string, model string, body map[string]any) string {
	payload, _ := json.Marshal(map[string]any{"kind": kind, "model": model, "request": body})
	digest := sha256.Sum256(payload)
	return hex.EncodeToString(digest[:])
}

func (s *Service) resolveEffectivePricing(ctx context.Context, candidate store.RuntimeModelCandidate) (resolvedPricing, error) {
	ruleSetID := firstNonEmptyString(
		candidate.ModelPricingRuleSetID,
		candidate.PlatformPricingRuleSetID,
		candidate.BasePricingRuleSetID,
	)
	if ruleSetID != "" {
		if s.store == nil {
			return resolvedPricing{}, &PricingUnavailableError{Reason: "pricing store is unavailable", RuleSetID: ruleSetID}
		}
		config, err := s.store.PricingRuleSetBillingConfigV2(ctx, ruleSetID)
		if err != nil {
			return resolvedPricing{}, &PricingUnavailableError{Reason: err.Error(), RuleSetID: ruleSetID}
		}
		if len(candidate.BillingConfigOverride) > 0 {
			config.Config = mergeMap(config.Config, candidate.BillingConfigOverride)
		}
		return resolvedPricing{
			Config: config.Config, Currency: config.Currency, RuleSetID: config.RuleSetID,
			RuleSetKey: config.RuleSetKey, Source: pricingRuleSource(candidate, ruleSetID),
			FreeResource: config.FreeResource, Snapshot: config.Snapshot,
		}, nil
	}

	config := candidate.BaseBillingConfig
	source := "base_model_config"
	if len(candidate.BillingConfig) > 0 {
		config = candidate.BillingConfig
		source = "platform_model_config"
	}
	if len(candidate.BillingConfigOverride) > 0 {
		config = mergeMap(config, candidate.BillingConfigOverride)
		source += "_override"
	}
	if len(config) == 0 {
		return resolvedPricing{}, &PricingUnavailableError{Reason: "candidate has no pricing configuration"}
	}
	freeResource := explicitFreeResources(config)
	snapshot := map[string]any{
		"pricingVersion": pricingVersionV2,
		"source":         source,
		"currency":       "resource",
		"config":         config,
	}
	return resolvedPricing{
		Config: config, Currency: "resource", Source: source,
		FreeResource: freeResource, Snapshot: snapshot,
	}, nil
}

func pricingRuleSource(candidate store.RuntimeModelCandidate, ruleSetID string) string {
	switch ruleSetID {
	case candidate.ModelPricingRuleSetID:
		return "platform_model_rule_set"
	case candidate.PlatformPricingRuleSetID:
		return "platform_rule_set"
	default:
		return "base_model_rule_set"
	}
}

func explicitFreeResources(config map[string]any) map[string]bool {
	resources := map[string]bool{}
	if isFree, _ := config["isFree"].(bool); isFree {
		for _, resource := range []string{"text_input", "text_cached_input", "text_output", "text_total", "image", "image_edit", "video", "music", "audio"} {
			resources[resource] = true
		}
	}
	for _, resource := range []string{"text", "text_total", "image", "image_edit", "video", "music", "audio"} {
		resourceConfig, _ := config[resource].(map[string]any)
		if isFree, _ := resourceConfig["isFree"].(bool); isFree {
			resources[resource] = true
		}
	}
	return resources
}

func (s *Service) estimateCandidateV2(ctx context.Context, user *auth.User, kind string, body map[string]any, candidate store.RuntimeModelCandidate) (candidateEstimate, error) {
	usage := clients.Usage{InputTokens: estimateRequestTokens(body)}
	if isTextGenerationKind(kind) {
		usage.OutputTokens = estimatedOutputTokens(body, candidate)
	}
	usage.TotalTokens = usage.InputTokens + usage.OutputTokens
	response := clients.Response{Usage: usage}
	items, total, pricing, err := s.billingsV2(ctx, user, kind, body, candidate, response, true)
	if err != nil {
		return candidateEstimate{}, err
	}
	if isTextBillingKind(kind) {
		if _, err := pricing.requiredTextPrice("text_cached_input", "textCachedInputPer1k", "cachedInputTokenPrice", "cacheInputTokenPrice", "textInputCacheHitPer1k", "inputCacheHitTokenPrice"); err != nil {
			return candidateEstimate{}, err
		}
	}
	return candidateEstimate{
		Items: items, Amount: total, Currency: pricing.Currency, Snapshot: pricing.Snapshot, Pricing: pricing,
	}, nil
}

func (s *Service) billingsV2(
	ctx context.Context,
	user *auth.User,
	kind string,
	body map[string]any,
	candidate store.RuntimeModelCandidate,
	response clients.Response,
	simulated bool,
) ([]any, fixedAmount, resolvedPricing, error) {
	pricing, err := s.resolveEffectivePricing(ctx, candidate)
	if err != nil {
		return nil, 0, resolvedPricing{}, err
	}
	return s.billingsWithResolvedPricingV2(ctx, user, kind, body, candidate, response, simulated, pricing)
}

func (s *Service) billingsWithResolvedPricingV2(
	ctx context.Context,
	user *auth.User,
	kind string,
	body map[string]any,
	candidate store.RuntimeModelCandidate,
	response clients.Response,
	simulated bool,
	pricing resolvedPricing,
) ([]any, fixedAmount, resolvedPricing, error) {
	discount, err := fixedAmountFromAny(effectiveDiscount(ctx, s.store, user, candidate))
	if err != nil || discount <= 0 {
		return nil, 0, resolvedPricing{}, &PricingUnavailableError{Reason: "invalid discount factor", RuleSetID: pricing.RuleSetID}
	}
	buildLine := func(resourceType string, unit string, quantity any, amount fixedAmount, details map[string]any) map[string]any {
		line := billingLineWithDetails(candidate, resourceType, unit, quantity, amount.Float64(), discount.Float64(), simulated, details)
		line["pricingVersion"] = pricingVersionV2
		line["pricingSource"] = pricing.Source
		if pricing.RuleSetID != "" {
			line["pricingRuleSetId"] = pricing.RuleSetID
		}
		line["isFree"] = amount.IsZero()
		return line
	}

	if isTextBillingKind(kind) {
		inputTokens := response.Usage.InputTokens
		outputTokens := response.Usage.OutputTokens
		cachedInputTokens := response.Usage.CachedInputTokens
		if isTextInputOnlyKind(kind) && inputTokens == 0 && response.Usage.TotalTokens > 0 {
			inputTokens = response.Usage.TotalTokens
		}
		if inputTokens == 0 && outputTokens == 0 {
			inputTokens = estimateRequestTokens(body)
			if isTextGenerationKind(kind) {
				outputTokens = 1
			}
		}
		if cachedInputTokens > inputTokens && inputTokens > 0 {
			cachedInputTokens = inputTokens
		}
		uncachedInputTokens := inputTokens - cachedInputTokens
		if uncachedInputTokens < 0 {
			uncachedInputTokens = 0
		}
		inputPrice, err := pricing.requiredTextPrice("text_input", "textInputPer1k", "inputTokenPrice", "basePrice")
		if err != nil {
			return nil, 0, resolvedPricing{}, err
		}
		items := make([]any, 0, 3)
		total := fixedAmount(0)
		if uncachedInputTokens > 0 || cachedInputTokens == 0 {
			amount, calculationErr := pricing.calculateRatio("text_input", inputPrice, uncachedInputTokens, 1000, discount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			total, calculationErr = pricing.add("text", total, amount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			items = append(items, buildLine("text_input", "1k_tokens", uncachedInputTokens, amount, map[string]any{
				"inputTokens": inputTokens, "uncachedInputTokens": uncachedInputTokens,
				"cachedInputTokens": cachedInputTokens, "pricePer1k": inputPrice.Float64(),
			}))
		}
		if cachedInputTokens > 0 {
			cachedPrice, priceErr := pricing.requiredTextPrice("text_cached_input", "textCachedInputPer1k", "cachedInputTokenPrice", "cacheInputTokenPrice", "textInputCacheHitPer1k", "inputCacheHitTokenPrice")
			if priceErr != nil {
				return nil, 0, resolvedPricing{}, priceErr
			}
			amount, calculationErr := pricing.calculateRatio("text_cached_input", cachedPrice, cachedInputTokens, 1000, discount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			total, calculationErr = pricing.add("text", total, amount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			items = append(items, buildLine("text_cached_input", "1k_tokens", cachedInputTokens, amount, map[string]any{
				"inputTokens": inputTokens, "uncachedInputTokens": uncachedInputTokens,
				"cachedInputTokens": cachedInputTokens, "pricePer1k": cachedPrice.Float64(),
			}))
		}
		if isTextGenerationKind(kind) {
			outputPrice, priceErr := pricing.requiredTextPrice("text_output", "textOutputPer1k", "outputTokenPrice", "basePrice")
			if priceErr != nil {
				return nil, 0, resolvedPricing{}, priceErr
			}
			amount, calculationErr := pricing.calculateRatio("text_output", outputPrice, outputTokens, 1000, discount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			total, calculationErr = pricing.add("text", total, amount)
			if calculationErr != nil {
				return nil, 0, resolvedPricing{}, calculationErr
			}
			items = append(items, buildLine("text_output", "1k_tokens", outputTokens, amount, map[string]any{"pricePer1k": outputPrice.Float64()}))
		}
		return items, total, pricing, nil
	}

	count := requestOutputCount(body)
	resource := "image"
	unit := "image"
	baseKey := "imageBase"
	if kind == "images.edits" {
		resource = "image_edit"
		baseKey = "editBase"
	}
	if kind == "images.vectorize" {
		resource = "image_vectorize"
		unit = "conversion"
		baseKey = "vectorizeBase"
	}
	if kind == "videos.upscales" {
		resource = "video_enhance"
		unit = "5s_video"
		baseKey = "videoEnhanceBase"
		inputs := resolveVideoEnhancePricingInputs(body, response)
		price, priceErr := pricing.requiredPrice(resource, baseKey, "basePrice")
		if priceErr != nil {
			return nil, 0, resolvedPricing{}, priceErr
		}
		rawAmount, details := videoEnhanceAmount(pricing.Config, inputs, float64(count), price.Float64())
		rawAmount = math.Ceil(rawAmount*1e8) / 1e8
		amount, amountErr := fixedAmountFromAny(rawAmount * discount.Float64())
		if amountErr != nil {
			return nil, 0, resolvedPricing{}, pricing.calculationError(resource, amountErr)
		}
		return []any{buildLine(resource, unit, float64(count)*inputs.DurationUnits, amount, details)}, amount, pricing, nil
	}
	if kind == "videos.generations" {
		resource = "video"
		unit = "5s_video"
		baseKey = "videoBase"
		duration, durationSource := billingDurationSeconds(body, response)
		audioEnabled, audioSource := billingAudioEnabled(body, response)
		durationUnits := videoDurationUnits(duration)
		durationFixed, durationErr := fixedAmountFromAny(duration)
		if durationErr != nil {
			return nil, 0, resolvedPricing{}, pricing.calculationError(resource, durationErr)
		}
		price, priceErr := pricing.requiredPrice(resource, baseKey, "basePrice")
		if priceErr != nil {
			return nil, 0, resolvedPricing{}, priceErr
		}
		resolutionWeight, weightErr := pricing.weight(resource, "resolutionWeights", firstNonEmptyString(stringFromMap(body, "resolution"), stringFromMap(body, "size")))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		audioWeight, weightErr := pricing.weight(resource, "audioWeights", boolWeightKey(audioEnabled))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		referenceVideoWeight, weightErr := pricing.weight(resource, "referenceVideoWeights", boolWeightKey(requestHasReferenceVideo(body)))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		voiceWeight, weightErr := pricing.weight(resource, "voiceSpecifiedWeights", boolWeightKey(requestHasVoiceID(body, audioEnabled)))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		amount, calculationErr := multiplyFixedProductRatio(
			price,
			[]int{count},
			[]fixedAmount{durationFixed, resolutionWeight, audioWeight, referenceVideoWeight, voiceWeight, discount},
			videoBillingUnitSeconds,
		)
		if calculationErr != nil {
			return nil, 0, resolvedPricing{}, pricing.calculationError(resource, calculationErr)
		}
		item := buildLine(resource, unit, videoDurationQuantity(duration, count), amount, map[string]any{
			"count": count, "audio": audioEnabled, "audioSource": audioSource,
			"durationSeconds": duration, "durationSource": durationSource,
			"durationUnit": "5s", "durationUnitCount": durationUnits,
		})
		return []any{item}, amount, pricing, nil
	}
	if kind == "song.generations" || kind == "music.generations" {
		resource = "music"
		unit = "song"
		baseKey = "musicBase"
	}
	if kind == "speech.generations" || kind == "voice.clone" {
		resource = "audio"
		unit = "character"
		baseKey = "audioBase"
		count = len([]rune(stringFromMap(body, "text")))
		if count <= 0 {
			count = 1
		}
	}
	price, err := pricing.requiredPrice(resource, baseKey, "basePrice")
	if err != nil {
		return nil, 0, resolvedPricing{}, err
	}
	amount, calculationErr := pricing.calculate(resource, price, []int{count})
	if calculationErr != nil {
		return nil, 0, resolvedPricing{}, calculationErr
	}
	if resource == "image" || resource == "image_edit" {
		qualityWeight, weightErr := pricing.weight(resource, "qualityWeights", stringFromMap(body, "quality"))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		sizeWeight, weightErr := pricing.weight(resource, "sizeWeights", stringFromMap(body, "size"))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		resolutionWeight, weightErr := pricing.weight(resource, "resolutionWeights", firstNonEmptyString(stringFromMap(body, "resolution"), stringFromMap(body, "size")))
		if weightErr != nil {
			return nil, 0, resolvedPricing{}, weightErr
		}
		amount, calculationErr = pricing.calculate(resource, amount, nil, qualityWeight, sizeWeight, resolutionWeight)
		if calculationErr != nil {
			return nil, 0, resolvedPricing{}, calculationErr
		}
	}
	amount, calculationErr = pricing.calculate(resource, amount, nil, discount)
	if calculationErr != nil {
		return nil, 0, resolvedPricing{}, calculationErr
	}
	return []any{buildLine(resource, unit, count, amount, nil)}, amount, pricing, nil
}

func (pricing resolvedPricing) requiredTextPrice(resource string, keys ...string) (fixedAmount, error) {
	price, found, err := pricing.price("text", keys...)
	if err != nil {
		return 0, &PricingUnavailableError{Reason: err.Error(), ResourceType: resource, RuleSetID: pricing.RuleSetID}
	}
	if found && price > 0 {
		return price, nil
	}
	if found && price == 0 && (pricing.FreeResource[resource] || pricing.FreeResource["text_total"] || pricing.FreeResource["text"]) {
		return 0, nil
	}
	return 0, &PricingUnavailableError{Reason: "missing, invalid, or not explicitly free", ResourceType: resource, RuleSetID: pricing.RuleSetID}
}

func maximumCandidateEstimate(estimates []candidateEstimate) (candidateEstimate, error) {
	if len(estimates) == 0 {
		return candidateEstimate{}, &PricingUnavailableError{Reason: "no candidate has effective pricing"}
	}
	maximum := estimates[0]
	for _, estimate := range estimates[1:] {
		if estimate.Currency != maximum.Currency {
			return candidateEstimate{}, &PricingUnavailableError{Reason: "candidate currencies do not match"}
		}
		if estimate.Amount > maximum.Amount {
			maximum = estimate
		}
	}
	return maximum, nil
}
