package model

import (
	"math"
	"sort"
	"strconv"
	"strings"

	"github.com/QuantumNous/new-api/setting/ratio_setting"
)

// VideoFlatClipTierRow 单档视频标价（已套用成本折扣+加价折扣的有效展示价，未乘用户分组倍率）。
type VideoFlatClipTierRow struct {
	UsdAfterChannelDiscount float64 `json:"usd_after_channel_discount"`
	Resolution              string  `json:"resolution,omitempty"`
	HasAudio                *bool   `json:"has_audio,omitempty"`
	Lane                    string  `json:"lane,omitempty"`
}

// VideoFlatClipPricingHint 多档视频分辨率价在定价卡片上的摘要（按条或按秒，见 BillingMode）：
// MinUsdAfterChannelDiscount = min(渠道规则价×成本折扣% + 全局规则价×加价折扣%)，与实扣公式一致；
// 前端再乘用户当前分组倍率后走 displayPrice。
// Tiers 为全部档位（同口径），供「查看更多价格」表格展示。
type VideoFlatClipPricingHint struct {
	MinUsdAfterChannelDiscount float64                `json:"min_usd_after_channel_discount"`
	Resolution                 string                 `json:"resolution,omitempty"`
	HasAudio                   *bool                  `json:"has_audio,omitempty"`
	Lane                       string                 `json:"lane,omitempty"`
	TierCount                  int                    `json:"tier_count"`
	Tiers                      []VideoFlatClipTierRow `json:"tiers,omitempty"`
	// BillingMode：per_item 为按条成片；per_second 为按秒（如 Seedance2.0 常见 text_to_video_per_second）。
	BillingMode string `json:"billing_mode,omitempty"`
}

type videoFlatTier struct {
	RawUSD   float64
	Res      string
	HasAudio *bool
	Lane     string
}

func laneOrderVideoFlat(l string) int {
	switch l {
	case "text_to_video":
		return 0
	case "image_to_video":
		return 1
	case "video_to_video":
		return 2
	case "text_to_video_legacy":
		return 3
	case "image_to_video_legacy":
		return 4
	case "video_to_video_input_legacy":
		return 5
	case "video_to_video_output_legacy":
		return 6
	case "text_to_video_per_second":
		return 10
	case "image_to_video_per_second":
		return 11
	case "video_to_video_per_second":
		return 12
	default:
		return 99
	}
}

func audioPtrRank(p *bool) int {
	if p == nil {
		return 2
	}
	if !*p {
		return 0
	}
	return 1
}

func tierLessVideoFlat(a, b videoFlatTier) bool {
	ar := strings.TrimSpace(strings.ToLower(a.Res))
	br := strings.TrimSpace(strings.ToLower(b.Res))
	if ar != br {
		return ar < br
	}
	if laneOrderVideoFlat(a.Lane) != laneOrderVideoFlat(b.Lane) {
		return laneOrderVideoFlat(a.Lane) < laneOrderVideoFlat(b.Lane)
	}
	return audioPtrRank(a.HasAudio) < audioPtrRank(b.HasAudio)
}

func tierDedupeKey(ti videoFlatTier) string {
	return ti.Lane + "\x00" + strings.TrimSpace(strings.ToLower(ti.Res)) + "\x00" +
		strconv.FormatFloat(ti.RawUSD, 'f', 8, 64)
}

// collapsePairedUnifiedAudioTiers 同 lane+分辨率+价格 下同时存在 has_audio true/false 时合并为一条（HasAudio=nil），供展示「统一」。
func collapsePairedUnifiedAudioTiers(tiers []videoFlatTier) []videoFlatTier {
	order := make([]string, 0)
	groups := make(map[string][]videoFlatTier)
	for _, ti := range tiers {
		k := tierDedupeKey(ti)
		if _, ok := groups[k]; !ok {
			order = append(order, k)
		}
		groups[k] = append(groups[k], ti)
	}
	out := make([]videoFlatTier, 0, len(tiers))
	for _, k := range order {
		g := groups[k]
		if len(g) == 2 {
			a, b := g[0], g[1]
			if a.HasAudio != nil && b.HasAudio != nil && *a.HasAudio != *b.HasAudio &&
				math.Abs(a.RawUSD-b.RawUSD) < 1e-9 &&
				strings.EqualFold(strings.TrimSpace(a.Res), strings.TrimSpace(b.Res)) &&
				a.Lane == b.Lane {
				out = append(out, videoFlatTier{
					RawUSD:   a.RawUSD,
					Res:      a.Res,
					Lane:     a.Lane,
					HasAudio: nil,
				})
				continue
			}
		}
		out = append(out, g...)
	}
	return out
}

// normalizeLegacyAllFalseToUnifiedHintTiers 旧版前端只写入 has_audio:false 的统一价；当每条档位均为 false（无 nil、无 true）时改为 nil 以便展示「统一」。
func normalizeLegacyAllFalseToUnifiedHintTiers(tiers []videoFlatTier) []videoFlatTier {
	if len(tiers) == 0 {
		return tiers
	}
	allExplicitFalse := true
	for i := range tiers {
		t := tiers[i]
		if t.HasAudio == nil {
			allExplicitFalse = false
			break
		}
		if *t.HasAudio {
			allExplicitFalse = false
			break
		}
	}
	if !allExplicitFalse {
		return tiers
	}
	out := make([]videoFlatTier, len(tiers))
	for i := range tiers {
		tt := tiers[i]
		tt.HasAudio = nil
		out[i] = tt
	}
	return out
}

func appendPerItemTiers(dst *[]videoFlatTier, rows []ratio_setting.VideoResolutionAudioPriceRule, lane string) {
	for i := range rows {
		r := rows[i]
		if r.Price <= 0 {
			continue
		}
		ha := r.HasAudio
		*dst = append(*dst, videoFlatTier{
			RawUSD:   r.Price,
			Res:      r.Resolution,
			HasAudio: &ha,
			Lane:     lane,
		})
	}
}

func appendLegacyPerVideoTiers(dst *[]videoFlatTier, rows []ratio_setting.VideoResolutionPerVideoRule, lane string) {
	for i := range rows {
		r := rows[i]
		if r.VideoPrice <= 0 {
			continue
		}
		*dst = append(*dst, videoFlatTier{
			RawUSD:   r.VideoPrice,
			Res:      r.Resolution,
			HasAudio: nil,
			Lane:     lane,
		})
	}
}

func collectVideoFlatTiers(rules ratio_setting.VideoPricingRules) []videoFlatTier {
	out := make([]videoFlatTier, 0, 48)
	appendPerItemTiers(&out, rules.TextToVideoPerItem, "text_to_video")
	appendPerItemTiers(&out, rules.ImageToVideoPerItem, "image_to_video")
	appendPerItemTiers(&out, rules.VideoToVideoPerItem, "video_to_video")
	appendLegacyPerVideoTiers(&out, rules.TextToVideoPerVideo, "text_to_video_legacy")
	appendLegacyPerVideoTiers(&out, rules.ImageToVideoPerVideo, "image_to_video_legacy")
	appendLegacyPerVideoTiers(&out, rules.VideoToVideoInputPerVideo, "video_to_video_input_legacy")
	appendLegacyPerVideoTiers(&out, rules.VideoToVideoOutputPerVideo, "video_to_video_output_legacy")
	return out
}

func collectVideoPerSecondTiers(rules ratio_setting.VideoPricingRules) []videoFlatTier {
	out := make([]videoFlatTier, 0, 24)
	appendPerItemTiers(&out, rules.TextToVideoPerSecond, "text_to_video_per_second")
	appendPerItemTiers(&out, rules.ImageToVideoPerSecond, "image_to_video_per_second")
	appendPerItemTiers(&out, rules.VideoToVideoPerSecond, "video_to_video_per_second")
	return out
}

func pickMinVideoFlatTier(tiers []videoFlatTier) (videoFlatTier, bool) {
	if len(tiers) == 0 {
		return videoFlatTier{}, false
	}
	best := 0
	for i := 1; i < len(tiers); i++ {
		a, b := tiers[best], tiers[i]
		if b.RawUSD < a.RawUSD-1e-12 {
			best = i
			continue
		}
		if math.Abs(b.RawUSD-a.RawUSD) < 1e-9 && tierLessVideoFlat(b, a) {
			best = i
		}
	}
	return tiers[best], true
}

func videoRulesUsableForPricingHint(rules ratio_setting.VideoPricingRules) bool {
	return ratio_setting.HasUsableVideoPerVideoRules(rules) ||
		ratio_setting.HasUsableVideoPerSecondRules(rules)
}

func resolveChannelVideoRulesForPricingCardHint(channelID int, modelName string) (ratio_setting.VideoPricingRules, bool) {
	if channelID > 0 {
		if rules, ok := ratio_setting.GetChannelVideoPricingRules(channelID, modelName); ok && videoRulesUsableForPricingHint(rules) {
			return rules, true
		}
	}
	return ratio_setting.VideoPricingRules{}, false
}

func resolveGlobalVideoRulesForPricingCardHint(modelName string) (ratio_setting.VideoPricingRules, bool) {
	if rules, ok := ratio_setting.GetVideoPricingRules(modelName); ok && videoRulesUsableForPricingHint(rules) {
		return rules, true
	}
	return ratio_setting.VideoPricingRules{}, false
}

func lookupVideoTierRawUSD(rules ratio_setting.VideoPricingRules, target videoFlatTier) float64 {
	if !videoRulesUsableForPricingHint(rules) {
		return 0
	}
	tiers := collectVideoFlatTiers(rules)
	if len(tiers) == 0 {
		tiers = collectVideoPerSecondTiers(rules)
	}
	for _, c := range tiers {
		if c.Lane != target.Lane {
			continue
		}
		if !strings.EqualFold(strings.TrimSpace(c.Res), strings.TrimSpace(target.Res)) {
			continue
		}
		if !videoTierAudioMatches(c.HasAudio, target.HasAudio) {
			continue
		}
		return c.RawUSD
	}
	return 0
}

func videoTierAudioMatches(a, b *bool) bool {
	if a == nil || b == nil {
		return true
	}
	return *a == *b
}

func effectiveVideoTierDisplayUSD(channelTier videoFlatTier, globalRules ratio_setting.VideoPricingRules, costDiscPercent, markupDiscPercent float64) float64 {
	globalRaw := lookupVideoTierRawUSD(globalRules, channelTier)
	return EffectiveRuleUnitPrice(channelTier.RawUSD, globalRaw, costDiscPercent, markupDiscPercent)
}

func tierRowLess(a, b VideoFlatClipTierRow) bool {
	ar := strings.TrimSpace(strings.ToLower(a.Resolution))
	br := strings.TrimSpace(strings.ToLower(b.Resolution))
	if ar != br {
		return ar < br
	}
	if laneOrderVideoFlat(a.Lane) != laneOrderVideoFlat(b.Lane) {
		return laneOrderVideoFlat(a.Lane) < laneOrderVideoFlat(b.Lane)
	}
	return audioPtrRank(a.HasAudio) < audioPtrRank(b.HasAudio)
}

func buildSortedTierRows(tiers []videoFlatTier, globalRules ratio_setting.VideoPricingRules, costDiscPercent, markupDiscPercent float64) []VideoFlatClipTierRow {
	rows := make([]VideoFlatClipTierRow, 0, len(tiers))
	for _, ti := range tiers {
		usd := effectiveVideoTierDisplayUSD(ti, globalRules, costDiscPercent, markupDiscPercent)
		if usd <= 0 {
			continue
		}
		var ha *bool
		if ti.HasAudio != nil {
			v := *ti.HasAudio
			ha = &v
		}
		rows = append(rows, VideoFlatClipTierRow{
			UsdAfterChannelDiscount: usd,
			Resolution:              strings.TrimSpace(ti.Res),
			HasAudio:                ha,
			Lane:                    ti.Lane,
		})
	}
	sort.Slice(rows, func(i, j int) bool {
		a, b := rows[i], rows[j]
		if math.Abs(a.UsdAfterChannelDiscount-b.UsdAfterChannelDiscount) > 1e-9 {
			return a.UsdAfterChannelDiscount < b.UsdAfterChannelDiscount
		}
		return tierRowLess(a, b)
	})
	return rows
}

func pickMinEffectiveVideoDisplayTier(tiers []videoFlatTier, globalRules ratio_setting.VideoPricingRules, costDiscPercent, markupDiscPercent float64) (videoFlatTier, float64, bool) {
	if len(tiers) == 0 {
		return videoFlatTier{}, 0, false
	}
	bestIdx := 0
	bestUsd := effectiveVideoTierDisplayUSD(tiers[0], globalRules, costDiscPercent, markupDiscPercent)
	for i := 1; i < len(tiers); i++ {
		u := effectiveVideoTierDisplayUSD(tiers[i], globalRules, costDiscPercent, markupDiscPercent)
		if u < bestUsd-1e-12 || (math.Abs(u-bestUsd) < 1e-9 && tierLessVideoFlat(tiers[i], tiers[bestIdx])) {
			bestIdx = i
			bestUsd = u
		}
	}
	if bestUsd <= 0 {
		return videoFlatTier{}, 0, false
	}
	return tiers[bestIdx], bestUsd, true
}

// BuildVideoFlatClipHint 汇总当前模型×渠道下视频分辨率档位（优先按条，否则按秒），返回最低价档（含成本折扣与加价折扣）及总档位数。
func BuildVideoFlatClipHint(channelID int, modelName string, costDiscPercent, markupDiscPercent float64) *VideoFlatClipPricingHint {
	channelRules, chOK := resolveChannelVideoRulesForPricingCardHint(channelID, modelName)
	globalRules, glOK := resolveGlobalVideoRulesForPricingCardHint(modelName)
	if !chOK && !glOK {
		return nil
	}
	rulesForTiers := channelRules
	if !chOK {
		rulesForTiers = globalRules
	}
	tiers := collectVideoFlatTiers(rulesForTiers)
	billingMode := "per_item"
	if len(tiers) == 0 {
		tiers = collectVideoPerSecondTiers(rulesForTiers)
		billingMode = "per_second"
	}
	if len(tiers) == 0 {
		return nil
	}
	tiers = collapsePairedUnifiedAudioTiers(tiers)
	tiers = normalizeLegacyAllFalseToUnifiedHintTiers(tiers)
	best, minUsd, ok := pickMinEffectiveVideoDisplayTier(tiers, globalRules, costDiscPercent, markupDiscPercent)
	if !ok {
		return nil
	}
	var hasAudioPtr *bool
	if best.HasAudio != nil {
		v := *best.HasAudio
		hasAudioPtr = &v
	}
	rows := buildSortedTierRows(tiers, globalRules, costDiscPercent, markupDiscPercent)
	return &VideoFlatClipPricingHint{
		MinUsdAfterChannelDiscount: minUsd,
		Resolution:                 strings.TrimSpace(best.Res),
		HasAudio:                   hasAudioPtr,
		Lane:                       best.Lane,
		TierCount:                  len(tiers),
		Tiers:                      rows,
		BillingMode:                billingMode,
	}
}
