package domain

import (
	"strconv"
	"strings"
)

// ProductMatchPreferred is the stable arbitration order from spec §3.3.
func ProductMatchPreferred(candidate, best *ProductMatch) bool {
	if candidate == nil || !candidate.Eligible || candidate.Excluded {
		return false
	}
	if best == nil || !best.Eligible || best.Excluded {
		return true
	}
	if candidate.ProductFitScore != best.ProductFitScore {
		return candidate.ProductFitScore > best.ProductFitScore
	}
	if candidate.ProductFitBand != best.ProductFitBand {
		return candidate.ProductFitBand == ProductFitBandStrong
	}
	if candidate.MatchedAt != best.MatchedAt {
		return candidate.MatchedAt < best.MatchedAt
	}
	return candidate.ProductID < best.ProductID
}

func BestEligibleProductMatch(matches []*ProductMatch) *ProductMatch {
	var best *ProductMatch
	for _, match := range matches {
		if ProductMatchPreferred(match, best) {
			best = match
		}
	}
	return best
}

// ProductFitToIntentFit keeps the existing five-question score scale while
// changing only its fit dimension when the primary product changes.
func ProductFitToIntentFit(productScore int) int {
	if productScore <= 0 {
		return 0
	}
	if productScore > 100 {
		productScore = 100
	}
	return (productScore*WeightFit + 50) / 100
}

// ApplyPrimaryProduct selects an eligible product (unless a human override is
// already present), copies its snapshot fields, and recomputes only the intent
// fit component. Other four reasons are retained verbatim.
func ApplyPrimaryProduct(o *Opportunity) {
	if o == nil {
		return
	}
	var primary *ProductMatch
	if o.PrimaryProductOverridden && o.PrimaryProductID != "" {
		for _, match := range o.ProductMatches {
			if match != nil && match.ProductID == o.PrimaryProductID {
				primary = match
				break
			}
		}
	} else {
		primary = BestEligibleProductMatch(o.ProductMatches)
	}
	if primary == nil {
		return
	}
	o.PrimaryBrandID, o.PrimaryProductID = primary.BrandID, primary.ProductID
	o.PrimaryBrandName, o.PrimaryProductLabel = primary.BrandNameSnapshot, primary.ProductLabelSnapshot
	o.PrimaryProductFitScore, o.PrimaryProductFitBand = primary.ProductFitScore, primary.ProductFitBand
	fitScore := ProductFitToIntentFit(primary.ProductFitScore)
	found := false
	for i := range o.Reasons {
		if o.Reasons[i].Dimension == DimFit {
			o.Reasons[i].Score = fitScore
			label := strings.TrimSpace(primary.ProductLabelSnapshot)
			if label == "" {
				label = primary.ProductID
			}
			o.Reasons[i].Reason = "主推產品「" + label + "」適配 " + formatProductScore(primary.ProductFitScore) + "，換算產品 fit " + formatProductScore(fitScore) + "/10"
			found = true
			break
		}
	}
	if !found {
		o.Reasons = append(o.Reasons, OpportunityReason{Dimension: DimFit, Score: fitScore, Reason: "主推產品適配分數換算產品 fit"})
	}
	total := 0
	for _, reason := range o.Reasons {
		total += reason.Score
	}
	o.IntentScore = SumReasonScores(o.Reasons)
	if total != o.IntentScore {
		o.IntentScore = total
		if o.IntentScore > 100 {
			o.IntentScore = 100
		}
	}
	o.IntentBand = BandFromScore(o.IntentScore)
}

func formatProductScore(score int) string {
	// Keep the helper local to avoid introducing a presentation dependency.
	if score < 0 {
		score = 0
	}
	return strconv.Itoa(score)
}
