package usecase

import (
	"context"
	"fmt"

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

// TodayStats is the high/mid/low count block for the today page.
type TodayStats struct {
	Total int
	High  int
	Mid   int
	Low   int
}

// TodayOpportunity is one card on the today page (opp + optional default reply).
type TodayOpportunity struct {
	Opportunity  *domain.Opportunity
	DefaultReply *domain.ReplyVariant
}

// TodayResult powers GET /radar/today.
type TodayResult struct {
	Stats          TodayStats
	High           []TodayOpportunity
	Mid            []TodayOpportunity
	Low            []TodayOpportunity
	TruncatedCount int
	LastSweptAt    int64
	EmptyReason    string
	EmptyHint      string
}

// todayListStatuses：今日名單含可操作與已處理，排除 rejected／judging（T545）。
var todayListStatuses = []string{domain.OppQualified, domain.OppAccepted, domain.OppDismissed}

func (s *Service) GetToday(ctx context.Context, ownerUID int64) (*TodayResult, error) {
	return s.GetTodayFiltered(ctx, ownerUID, domain.OpportunityListFilter{})
}

func (s *Service) GetTodayFiltered(ctx context.Context, ownerUID int64, productFilter domain.OpportunityListFilter) (*TodayResult, error) {
	if ownerUID <= 0 {
		return nil, fmt.Errorf("%w: owner_uid required", domain.ErrValidation)
	}
	start, end := domain.UTCDayBounds(domain.NowNano())

	watches, _, err := s.Repo.ListWatches(ctx, ownerUID, domain.WatchListFilter{Page: 1, PageSize: 50})
	if err != nil {
		return nil, err
	}
	active, err := s.Repo.ListActiveWatches(ctx, ownerUID)
	if err != nil {
		return nil, err
	}
	var lastSwept int64
	for _, w := range watches {
		if w.LastSweptAt > lastSwept {
			lastSwept = w.LastSweptAt
		}
	}

	list, _, err := s.Repo.ListOpportunities(ctx, ownerUID, domain.OpportunityListFilter{
		Statuses:    todayListStatuses,
		BrandID:     productFilter.BrandID,
		ProductID:   productFilter.ProductID,
		FitBand:     productFilter.FitBand,
		CreatedFrom: start,
		CreatedTo:   end,
		Page:        1,
		PageSize:    100,
	})
	if err != nil {
		return nil, err
	}
	productFiltered := productFilter.BrandID != "" || productFilter.ProductID != "" || productFilter.FitBand != ""

	visible := make([]*domain.Opportunity, 0, len(list))
	for _, o := range list {
		if len(o.ProductMatches) > 0 && !todayHasEligibleProduct(o, productFilter) {
			continue
		}
		if productFiltered && len(o.ProductMatches) == 0 {
			continue
		}
		visible = append(visible, o)
	}
	visibleIDs := make([]string, 0, len(visible))
	for _, o := range visible {
		visibleIDs = append(visibleIDs, o.ID)
	}
	// 一次取回整頁的回覆；預設回覆只是輔助資訊，查不到不擋今日名單。
	repliesByOpportunity, rerr := s.Repo.ListRepliesForOpportunities(ctx, ownerUID, visibleIDs)
	if rerr != nil {
		repliesByOpportunity = nil
	}

	high, mid, low := []TodayOpportunity{}, []TodayOpportunity{}, []TodayOpportunity{}
	for _, o := range visible {
		card := TodayOpportunity{Opportunity: o}
		for _, r := range repliesByOpportunity[o.ID] {
			if r.Variant == domain.ReplyPublicComment {
				card.DefaultReply = r
				break
			}
		}
		switch o.IntentBand {
		case domain.BandHigh:
			high = append(high, card)
		case domain.BandMid:
			mid = append(mid, card)
		default:
			low = append(low, card)
		}
	}
	total := len(high) + len(mid) + len(low)

	// truncated from today's sweeps
	sweeps, _, _ := s.Repo.ListSweeps(ctx, ownerUID, domain.SweepListFilter{Page: 1, PageSize: 20})
	trunc := 0
	var latestFail string
	for _, sw := range sweeps {
		if sw.StartedAt >= start && sw.StartedAt < end {
			trunc += sw.TruncatedCount
			if sw.FailedReason != "" {
				latestFail = sw.FailedReason
			}
		}
	}

	out := &TodayResult{
		Stats:          TodayStats{Total: total, High: len(high), Mid: len(mid), Low: len(low)},
		High:           high,
		Mid:            mid,
		Low:            low,
		TruncatedCount: trunc,
		LastSweptAt:    lastSwept,
	}
	if total == 0 {
		if productFiltered {
			out.EmptyReason, out.EmptyHint = "no_eligible_product_match", "今日沒有符合所選產品且達到可跟進門檻的商機。"
		} else {
			out.EmptyReason, out.EmptyHint = emptyReason(len(watches), len(active), lastSwept, latestFail, start)
		}
	}
	return out, nil
}

func todayHasEligibleProduct(o *domain.Opportunity, f domain.OpportunityListFilter) bool {
	for _, match := range o.ProductMatches {
		if match == nil || !match.Eligible || match.Excluded {
			continue
		}
		if f.BrandID != "" && match.BrandID != f.BrandID {
			continue
		}
		if f.ProductID != "" && match.ProductID != f.ProductID {
			continue
		}
		if f.FitBand != "" && match.ProductFitBand != f.FitBand {
			continue
		}
		return true
	}
	return false
}

func emptyReason(watchCount, activeCount int, lastSwept int64, fail string, dayStart int64) (reason, hint string) {
	if watchCount == 0 {
		return "no_watch", "建立至少一組關鍵字訂閱，明天早晨就會開始巡。"
	}
	if activeCount == 0 {
		return "all_watches_paused", "目前沒有啟用中的訂閱。恢復一組訂閱後才會繼續巡。"
	}
	if fail != "" {
		return "sweep_failed", fail
	}
	if lastSwept < dayStart {
		return "not_swept_yet", "今日巡檢還沒跑完（每日 UTC 22:00 開始）。也可在訂閱頁手動觸發。"
	}
	return "no_hit", "這輪有巡但沒有符合的商機。可放寬關鍵字或檢查排除詞是否太嚴。"
}
