package usecase

import (
	"context"
	"sort"

	"apps/backend/internal/module/crm/domain"
	radarDomain "apps/backend/internal/module/radar/domain"
)

// StatsMinSample 是能公布比率的最低樣本數（spec §9.5：樣本 < 5 只給絕對數）。
const StatsMinSample = 5

// DimensionVariants 目前無法計算：回覆版本與成交之間還沒有歸因欄位，
// 硬回空陣列會讓前端把功能缺口顯示成「使用者還沒有資料」。
const DimensionVariants = "variants"

// DimensionTerms／DimensionSources 需要 radar 商機才能歸因。
const (
	DimensionTerms   = "terms"
	DimensionSources = "sources"
)

// ConversionStat 只帶絕對數；比率是否揭露由樣本門檻決定。
type ConversionStat struct {
	Key      string
	Accepted int
	Replied  int
	Won      int
}

// StatsResult 是 CRM 轉換統計。UnavailableDimensions 明確標出「尚未實作／無法計算」
// 的維度，讓呼叫端能跟「查得到但沒有資料」區分開來。
type StatsResult struct {
	TotalContacts         int64
	ByStage               map[string]int
	NeedsFollowUp         int
	Terms                 []ConversionStat
	Sources               []ConversionStat
	UnavailableDimensions []string
}

/*
Stats 聚合 CRM 轉換統計；from／to 任一邊為 0 代表該側不設限。

兩種維度的時間語意不同，因為問的問題不同：
  - 名單分佈看聯絡人「最近活動時間」落在區間內。
  - 關鍵字／來源看商機的「建立時間」落在區間內，成交與否則取該聯絡人的現況階段。
*/
func (s *Service) Stats(ctx context.Context, ownerUID int64, from, to int64) (*StatsResult, error) {
	out := &StatsResult{
		ByStage: map[string]int{},
		Terms:   []ConversionStat{},
		Sources: []ConversionStat{},
		// 回覆版本維度缺資料模型支援，永遠標為不可用。
		UnavailableDimensions: []string{DimensionVariants},
	}

	stageByContact, err := s.collectContactStages(ctx, ownerUID, from, to, out)
	if err != nil {
		return nil, err
	}
	out.ByStage["needs_follow_up"] = out.NeedsFollowUp

	if s.RadarOpps == nil {
		out.UnavailableDimensions = append(out.UnavailableDimensions, DimensionTerms, DimensionSources)
		return out, nil
	}
	terms, sources, err := s.attributeOpportunities(ctx, ownerUID, from, to, stageByContact)
	if err != nil {
		return nil, err
	}
	out.Terms, out.Sources = sortedStats(terms), sortedStats(sources)
	return out, nil
}

// collectContactStages 走訪全部聯絡人：區間內的計入名單分佈，
// 同時建立 contact → stage 對照供商機歸因使用（避免逐筆回查）。
func (s *Service) collectContactStages(
	ctx context.Context, ownerUID, from, to int64, out *StatsResult,
) (map[string]string, error) {
	const pageSize = 500
	stageByContact := map[string]string{}
	scanned := 0
	for page := 1; ; page++ {
		list, total, err := s.Repo.ListContacts(ctx, ownerUID, domain.ContactListFilter{
			Page: page, PageSize: pageSize,
		})
		if err != nil {
			return nil, err
		}
		scanned += len(list)
		for _, c := range list {
			stageByContact[c.ID] = c.Stage
			if !withinRange(contactActivityAt(c), from, to) {
				continue
			}
			out.ByStage[c.Stage]++
			if c.NeedsFollowUp {
				out.NeedsFollowUp++
			}
			out.TotalContacts++
		}
		if len(list) == 0 || int64(scanned) >= total {
			break
		}
	}
	return stageByContact, nil
}

// attributeOpportunities 把已接受的商機分攤到關鍵字與來源兩個維度。
// 同一個聯絡人在同一關鍵字下只算一次，否則多筆商機會灌大 accepted。
func (s *Service) attributeOpportunities(
	ctx context.Context, ownerUID, from, to int64, stageByContact map[string]string,
) (map[string]*ConversionStat, map[string]*ConversionStat, error) {
	const pageSize = 200
	terms := map[string]*ConversionStat{}
	sources := map[string]*ConversionStat{}
	countedTerm := map[string]bool{}
	countedSource := map[string]bool{}
	scanned := 0
	for page := 1; ; page++ {
		list, total, err := s.RadarOpps.ListOpportunities(ctx, ownerUID, radarDomain.OpportunityListFilter{
			Statuses:    []string{radarDomain.OppAccepted},
			CreatedFrom: from,
			CreatedTo:   to,
			Page:        page,
			PageSize:    pageSize,
		})
		if err != nil {
			return nil, nil, err
		}
		scanned += len(list)
		for _, o := range list {
			if o == nil || o.ContactID == "" {
				continue
			}
			stage, ok := stageByContact[o.ContactID]
			if !ok {
				continue
			}
			for _, term := range o.MatchedTerms {
				if term == "" || countedTerm[o.ContactID+"\x00"+term] {
					continue
				}
				countedTerm[o.ContactID+"\x00"+term] = true
				tally(terms, term, stage)
			}
			source := conversionSource(o.Source)
			if !countedSource[o.ContactID+"\x00"+source] {
				countedSource[o.ContactID+"\x00"+source] = true
				tally(sources, source, stage)
			}
		}
		if len(list) == 0 || int64(scanned) >= total {
			break
		}
	}
	return terms, sources, nil
}

func tally(into map[string]*ConversionStat, key, stage string) {
	stat := into[key]
	if stat == nil {
		stat = &ConversionStat{Key: key}
		into[key] = stat
	}
	stat.Accepted++
	if stageReachedReply(stage) {
		stat.Replied++
	}
	if stage == domain.StageWon {
		stat.Won++
	}
}

// stageReachedReply：現況階段已走到「對方回覆」之後才算 replied。
func stageReachedReply(stage string) bool {
	switch stage {
	case domain.StageReplied, domain.StageQuoted, domain.StageWon:
		return true
	}
	return false
}

// conversionSource 把商機來源翻成統計維度的來源名稱。
func conversionSource(oppSource string) string {
	switch oppSource {
	case radarDomain.OppSourceScoutPromote:
		return "scout"
	case radarDomain.OppSourceManualImport:
		return "manual_import"
	default:
		return "radar"
	}
}

func contactActivityAt(c *domain.Contact) int64 {
	if c.LastTouchAt > 0 {
		return c.LastTouchAt
	}
	if c.UpdatedAt > 0 {
		return c.UpdatedAt
	}
	return c.CreatedAt
}

func withinRange(at, from, to int64) bool {
	if from > 0 && at < from {
		return false
	}
	if to > 0 && at > to {
		return false
	}
	return true
}

// sortedStats 以成交數→接受數→名稱排序，讓輸出穩定且高價值的排前面。
func sortedStats(in map[string]*ConversionStat) []ConversionStat {
	out := make([]ConversionStat, 0, len(in))
	for _, stat := range in {
		out = append(out, *stat)
	}
	sort.Slice(out, func(i, j int) bool {
		if out[i].Won != out[j].Won {
			return out[i].Won > out[j].Won
		}
		if out[i].Accepted != out[j].Accepted {
			return out[i].Accepted > out[j].Accepted
		}
		return out[i].Key < out[j].Key
	})
	return out
}
