package databi

// 幸运礼物V3 section：数据来自 lucky-gift-service 的按日读模型（lucky_draw_pool_day_stats），
// 不走 statistics-service。注入方式与 Finance overlay 同层：appRequirements 组装 sections 时追加。
// 口径：只统计 dynamic_v3 策略族的房间抽奖（不含外部接入），返奖为用户可见金额（含未发放/失败行）。

import (
	"context"
	"strings"
	"time"

	luckygiftv1 "hyapp.local/api/proto/luckygift/v1"
)

const luckyGiftV3SectionKey = "lucky_gift_v3"

// luckyGiftV3MaxDays 与 lucky-gift-service 侧的窗口上限对齐，防御异常长区间把零填充行撑爆。
const luckyGiftV3MaxDays = 400

// luckyGiftStatZone 与 lucky_draw_pool_day_stats 的北京日分桶一致；FixedZone 规避容器缺 tzdata。
var luckyGiftStatZone = time.FixedZone("Asia/Shanghai", 8*60*60)

// LuckyGiftDailyStatsSource 是 databi 对幸运礼物统计的最小依赖面；luckygiftadmin.Client 天然满足。
type LuckyGiftDailyStatsSource interface {
	ListLuckyGiftDailyStats(ctx context.Context, req *luckygiftv1.ListLuckyGiftDailyStatsRequest) (*luckygiftv1.ListLuckyGiftDailyStatsResponse, error)
}

// WithLuckyGiftDailyStats 注入幸运礼物按日统计源；不注入时数据需求宽表没有幸运礼物V3板块。
func WithLuckyGiftDailyStats(source LuckyGiftDailyStatsSource, requestTimeout time.Duration) ServiceOption {
	return func(s *Service) {
		s.luckyGiftStats = source
		s.luckyGiftTimeout = requestTimeout
	}
}

// requirementsIncludesLuckyGiftV3 与 requirementsIncludesRevenue 同语义：空/all 视为命中。
func requirementsIncludesLuckyGiftV3(section string) bool {
	switch strings.ToLower(strings.TrimSpace(section)) {
	case "", "all", luckyGiftV3SectionKey:
		return true
	}
	return false
}

// requirementsOnlyLuckyGiftV3 为真时整个请求只要幸运礼物板块；此时必须跳过 statistics-service——
// 它不认识该 section key，会回退成全量六板块的最重查询路径。
func requirementsOnlyLuckyGiftV3(section string) bool {
	return strings.ToLower(strings.TrimSpace(section)) == luckyGiftV3SectionKey
}

// luckyGiftV3RegionIDs 与 Finance overlay 相同的区域参数收敛：单区域查询归一化后覆盖多区域列表。
func luckyGiftV3RegionIDs(query RequirementsQuery, regions []RegionInfo, regionIDs []int64) []int64 {
	if query.RegionID > 0 {
		return []int64{normalizeRegionID(regions, query.RegionID)}
	}
	return regionIDs
}

// luckyGiftV3StatDays 按北京日展开 [startMS, endMS) 覆盖的日列表，用于零填充。
func luckyGiftV3StatDays(startMS, endMS int64) []string {
	if endMS <= startMS {
		return nil
	}
	start := time.UnixMilli(startMS).In(luckyGiftStatZone)
	end := time.UnixMilli(endMS - 1).In(luckyGiftStatZone)
	startDay := time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, luckyGiftStatZone)
	endDay := time.Date(end.Year(), end.Month(), end.Day(), 0, 0, 0, 0, luckyGiftStatZone)
	days := make([]string, 0, 32)
	for day := startDay; !day.After(endDay) && len(days) < luckyGiftV3MaxDays; day = day.AddDate(0, 0, 1) {
		days = append(days, day.Format("2006-01-02"))
	}
	return days
}

// luckyGiftV3Row 组装单行；比例在投入为 0 时输出 nil（前端渲染 "--"），不伪造 0%。
func luckyGiftV3Row(statDay, label string, drawCount, turnoverCoin, payoutCoin float64) map[string]any {
	row := map[string]any{
		"stat_day":      statDay,
		"label":         label,
		"draw_count":    drawCount,
		"turnover_coin": turnoverCoin,
		"payout_coin":   payoutCoin,
		"profit_coin":   turnoverCoin - payoutCoin,
	}
	if turnoverCoin > 0 {
		row["payout_rate"] = payoutCoin / turnoverCoin
		row["profit_rate"] = (turnoverCoin - payoutCoin) / turnoverCoin
	} else {
		row["payout_rate"] = nil
		row["profit_rate"] = nil
	}
	return row
}

func luckyGiftV3Columns() []map[string]any {
	return []map[string]any{
		{"key": "draw_count", "label": "抽奖次数", "type": "count", "tooltip": "dynamic_v3 策略下的房间付费抽奖次数（不含外部 App 接入）"},
		{"key": "turnover_coin", "label": "投入金币", "type": "coin", "tooltip": "抽奖消耗金币"},
		{"key": "payout_coin", "label": "返奖金币", "type": "coin", "tooltip": "用户可见返奖金币，含未发放/失败行，与幸运礼物后台汇总口径一致"},
		{"key": "profit_coin", "label": "利润金币", "type": "coin", "tooltip": "投入金币 - 返奖金币"},
		{"key": "payout_rate", "label": "返奖率", "type": "ratio", "tooltip": "返奖金币 / 投入金币（实际 RTP）"},
		{"key": "profit_rate", "label": "利润率", "type": "ratio", "tooltip": "利润金币 / 投入金币"},
	}
}

// luckyGiftV3Section 拉取 dynamic_v3 按日统计并组装成数据需求 section。
// 数据按北京日零填充整个查询窗口：v3 未开量时明确展示 0，而不是空表让人误以为接口失败。
func (s *Service) luckyGiftV3Section(ctx context.Context, appCode string, query RequirementsQuery, regionIDs []int64) (map[string]any, error) {
	if s.luckyGiftTimeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, s.luckyGiftTimeout)
		defer cancel()
	}
	resp, err := s.luckyGiftStats.ListLuckyGiftDailyStats(ctx, &luckygiftv1.ListLuckyGiftDailyStatsRequest{
		Meta:            &luckygiftv1.RequestMeta{Caller: "admin-server", AppCode: appCode, SentAtMs: time.Now().UnixMilli()},
		StrategyVersion: "dynamic_v3",
		StartMs:         query.StartMS,
		EndMs:           query.EndMS,
		RegionIds:       regionIDs,
	})
	if err != nil {
		return nil, err
	}
	byDay := map[string]*luckygiftv1.LuckyGiftDailyStat{}
	for _, stat := range resp.GetStats() {
		byDay[stat.GetStatDay()] = stat
	}
	days := luckyGiftV3StatDays(query.StartMS, query.EndMS)
	rows := make([]map[string]any, 0, len(days))
	var totalDraws, totalTurnover, totalPayout int64
	for _, day := range days {
		stat := byDay[day]
		rows = append(rows, luckyGiftV3Row(day, day, float64(stat.GetDrawCount()), float64(stat.GetTurnoverCoin()), float64(stat.GetPayoutCoin())))
		totalDraws += stat.GetDrawCount()
		totalTurnover += stat.GetTurnoverCoin()
		totalPayout += stat.GetPayoutCoin()
	}
	section := map[string]any{
		"key":   luckyGiftV3SectionKey,
		"label": "幸运礼物V3",
		// 幸运礼物没有用户身份/付费身份/新老用户维度，筛选恒为 all；前端该 tab 不渲染筛选器。
		"filters":          map[string]any{"user_role": "all", "payer_type": "all", "new_user_type": "all"},
		"strategy_version": "dynamic_v3",
		"columns":          luckyGiftV3Columns(),
		"metric_definitions": []map[string]any{
			{"metric": "draw_count", "definition": "dynamic_v3 策略规则版本下的房间付费抽奖次数"},
			{"metric": "payout_coin", "definition": "用户可见返奖（effective），含未发放/失败行；口径与幸运礼物后台汇总一致"},
			{"metric": "payout_rate", "definition": "汇总/平均值行的比例按区间合计重新计算，不是逐日比例的算术平均"},
		},
		"total":        luckyGiftV3Row("total", "汇总", float64(totalDraws), float64(totalTurnover), float64(totalPayout)),
		"daily_series": rows,
	}
	if dayCount := len(days); dayCount > 0 {
		average := luckyGiftV3Row("average", "平均值",
			float64(totalDraws)/float64(dayCount),
			float64(totalTurnover)/float64(dayCount),
			float64(totalPayout)/float64(dayCount),
		)
		section["average"] = average
	}
	return section, nil
}
