package admin

import (
	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/validation"
	"bindbox-game/internal/repository/mysql/model"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

type activityProfitLossRequest struct {
	Page     int    `form:"page"`
	PageSize int    `form:"page_size"`
	Name     string `form:"name"`
	Status   int32  `form:"status"`  // 1进行中 2下线
	SortBy   string `form:"sort_by"` // profit, profit_asc, profit_rate, draw_count
}

type activityProfitLossItem struct {
	ActivityID         int64   `json:"activity_id"`
	ActivityName       string  `json:"activity_name"`
	Status             int32   `json:"status"`
	DrawCount          int64   `json:"draw_count"`
	GamePassCount      int64   `json:"game_pass_count"` // 次卡抽奖次数
	PaymentCount       int64   `json:"payment_count"`   // 现金/优惠券抽奖次数
	RefundCount        int64   `json:"refund_count"`    // 退款/取消抽奖次数
	PlayerCount        int64   `json:"player_count"`
	TotalRevenue       int64   `json:"total_revenue"`         // 实际支付金额 (分)
	TotalDiscount      int64   `json:"total_discount"`        // 优惠券抵扣金额 (分)
	TotalGamePassValue int64   `json:"total_game_pass_value"` // 次卡价值 (分)
	TotalCost          int64   `json:"total_cost"`            // 奖品标价总和 (分)
	Profit             int64   `json:"profit"`                // (Revenue + Discount + GamePassValue) - Cost
	ProfitRate         float64 `json:"profit_rate"`           // Profit / (Revenue + Discount + GamePassValue)
}

type activityProfitLossResponse struct {
	Page     int                      `json:"page"`
	PageSize int                      `json:"page_size"`
	Total    int64                    `json:"total"`
	List     []activityProfitLossItem `json:"list"`
}

func (h *handler) DashboardActivityProfitLoss() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(activityProfitLossRequest)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		if req.Page <= 0 {
			req.Page = 1
		}
		if req.PageSize <= 0 {
			req.PageSize = 20
		}

		db := h.repo.GetDbR().WithContext(ctx.RequestContext())

		// 1. 获取活动列表基础信息
		// 1. 获取活动列表基础信息
		var activities []model.Activities
		// 仅查询有完整配置(Issue->RewardSettings)且未删除的活动
		// 使用 Raw SQL 避免 GORM 自动注入 ambiguous 的 deleted_at
		rawSubQuery := fmt.Sprintf(`
			SELECT activity_issues.activity_id 
			FROM %s AS activity_issues 
			JOIN %s AS activity_reward_settings ON activity_reward_settings.issue_id = activity_issues.id 
			WHERE activity_issues.deleted_at IS NULL 
			AND activity_reward_settings.deleted_at IS NULL
		`, model.TableNameActivityIssues, model.TableNameActivityRewardSettings)

		query := db.Table(model.TableNameActivities).
			Where("activities.deleted_at IS NULL").
			Where(fmt.Sprintf("activities.id IN (%s)", rawSubQuery))

		if req.Name != "" {
			query = query.Where("activities.name LIKE ?", "%"+req.Name+"%")
		}
		if req.Status > 0 {
			query = query.Where("activities.status = ?", req.Status)
		}

		var total int64
		query.Count(&total)

		// 如果有排序需求，先获取所有活动计算盈亏后排序，再分页
		// 如果没有排序需求，直接数据库分页
		needCustomSort := req.SortBy != ""
		var limitQuery = query
		if !needCustomSort {
			limitQuery = query.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize)
		}

		if err := limitQuery.Order("id DESC").Find(&activities).Error; err != nil {
			h.logger.Error(fmt.Sprintf("GetActivityProfitLoss activities error: %v", err))
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21021, err.Error()))
			return
		}

		if len(activities) == 0 {
			ctx.Payload(&activityProfitLossResponse{
				Page:     req.Page,
				PageSize: req.PageSize,
				Total:    total,
				List:     []activityProfitLossItem{},
			})
			return
		}

		activityIDs := make([]int64, len(activities))
		activityMap := make(map[int64]*activityProfitLossItem)
		for i, a := range activities {
			activityIDs[i] = a.ID
			activityMap[a.ID] = &activityProfitLossItem{
				ActivityID:   a.ID,
				ActivityName: a.Name,
				Status:       a.Status,
			}
		}

		// 2. 统计抽奖次数和人数 (通过 activity_draw_logs 关联 activity_issues 和 orders)
		type drawStat struct {
			ActivityID    int64
			TotalCount    int64
			GamePassCount int64
			PaymentCount  int64
			RefundCount   int64
			PlayerCount   int64
		}
		var drawStats []drawStat
		db.Table(model.TableNameActivityDrawLogs).
			Select(`
				activity_issues.activity_id, 
				COUNT(activity_draw_logs.id) as total_count,
				SUM(CASE WHEN orders.status = 2 AND (orders.source_type = 4 OR orders.order_no LIKE 'GP%') THEN 1 ELSE 0 END) as game_pass_count,
				SUM(CASE WHEN orders.status = 2 AND NOT (orders.source_type = 4 OR orders.order_no LIKE 'GP%') THEN 1 ELSE 0 END) as payment_count,
				SUM(CASE WHEN orders.status IN (3, 4) THEN 1 ELSE 0 END) as refund_count,
				COUNT(DISTINCT CASE WHEN orders.status = 2 THEN activity_draw_logs.user_id END) as player_count
			`).
			Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
			Joins("LEFT JOIN orders ON orders.id = activity_draw_logs.order_id").
			Where("activity_issues.activity_id IN ?", activityIDs).
			Group("activity_issues.activity_id").
			Scan(&drawStats)

		for _, s := range drawStats {
			if item, ok := activityMap[s.ActivityID]; ok {
				item.DrawCount = s.GamePassCount + s.PaymentCount // 仅统计有效抽奖(次卡+支付)
				item.GamePassCount = s.GamePassCount
				item.PaymentCount = s.PaymentCount
				item.RefundCount = s.RefundCount
				item.PlayerCount = s.PlayerCount
			}
		}

		// 3. 统计营收和优惠券抵扣 (通过 orders 关联 activity_draw_logs)
		// 3. 统计营收和优惠券抵扣 (通过 orders 关联 activity_draw_logs)
		// BUG修复：排除已退款订单(status=4)。
		// 注意: MySQL SUM()运算涉及除法时会返回Decimal类型，需要Scan到float64
		type revenueStat struct {
			ActivityID    int64
			TotalRevenue  float64
			TotalDiscount float64
		}
		var revenueStats []revenueStat

		// 修正: 按抽奖次数比例分摊订单金额 (解决多活动订单归因问题)
		// 逻辑: 活动分摊收入 = 订单实际金额 * (该活动在该订单中的抽奖次数 / 该订单总抽奖次数)
		var err error
		err = db.Table(model.TableNameOrders).
			Select(`
				order_activity_draws.activity_id, 
				SUM(1.0 * orders.actual_amount * order_activity_draws.draw_count / order_total_draws.total_count) as total_revenue, 
				SUM(CASE WHEN orders.source_type = 4 OR orders.order_no LIKE 'GP%' THEN 0 ELSE 1.0 * orders.discount_amount * order_activity_draws.draw_count / order_total_draws.total_count END) as total_discount
			`).
			// Subquery 1: Calculate draw counts per order per activity (and link to issue->activity)
			Joins(`JOIN (
				SELECT activity_draw_logs.order_id, activity_issues.activity_id, COUNT(*) as draw_count
				FROM activity_draw_logs
				JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id
				GROUP BY activity_draw_logs.order_id, activity_issues.activity_id
			) as order_activity_draws ON order_activity_draws.order_id = orders.id`).
			// Subquery 2: Calculate total draw counts per order
			Joins(`JOIN (
				SELECT order_id, COUNT(*) as total_count
				FROM activity_draw_logs
				GROUP BY order_id
			) as order_total_draws ON order_total_draws.order_id = orders.id`).
			Where("orders.status = ?", 2). // 已支付（排除待支付、取消、退款状态）
			Where("order_activity_draws.activity_id IN ?", activityIDs).
			Group("order_activity_draws.activity_id").
			Scan(&revenueStats).Error

		if err != nil {
			h.logger.Error(fmt.Sprintf("GetActivityProfitLoss revenue stats error: %v", err))
		}

		for _, s := range revenueStats {
			if item, ok := activityMap[s.ActivityID]; ok {
				item.TotalRevenue = int64(s.TotalRevenue)
				item.TotalDiscount = int64(s.TotalDiscount)
			}
		}

		// 4. 统计成本 (通过 user_inventory 关联 products 和 orders)
		// 修正：增加关联 orders 表，过滤掉已退款/取消的订单 (status!=2)
		type costStat struct {
			ActivityID int64
			TotalCost  int64
		}
		var costStats []costStat
		db.Table(model.TableNameUserInventory).
			Select("user_inventory.activity_id, SUM(products.price) as total_cost").
			Joins("JOIN products ON products.id = user_inventory.product_id").
			Joins("LEFT JOIN orders ON orders.id = user_inventory.order_id").
			Where("user_inventory.activity_id IN ?", activityIDs).
			Where("orders.status = ?", 2). // 仅统计已支付订单产生的成本
			Group("user_inventory.activity_id").
			Scan(&costStats)

		for _, s := range costStats {
			if item, ok := activityMap[s.ActivityID]; ok {
				item.TotalCost = s.TotalCost
			}
		}

		// 5. 统计次卡价值 (0元订单按活动单价计算)
		// 先获取各活动的单价
		activityPriceMap := make(map[int64]int64)
		for _, a := range activities {
			activityPriceMap[a.ID] = a.PriceDraw
		}

		// 统计每个活动的0元订单对应的抽奖次数 (次卡支付)
		// BUG修复：之前统计的是订单数量，但一个订单可能包含多次抽奖
		// 正确做法是统计抽奖次数，再乘以活动单价
		type gamePassStat struct {
			ActivityID    int64
			GamePassDraws int64 // 抽奖次数，非订单数
		}
		var gamePassStats []gamePassStat
		db.Table(model.TableNameActivityDrawLogs).
			Select("activity_issues.activity_id, COUNT(activity_draw_logs.id) as game_pass_draws").
			Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
			Joins("JOIN orders ON orders.id = activity_draw_logs.order_id").
			Where("orders.status = ? AND orders.status != ?", 2, 4).       // 已支付且未退款
			Where("orders.actual_amount = 0").                             // 0元订单
			Where("orders.source_type = 4 OR orders.order_no LIKE 'GP%'"). // 次数卡 (Lottery SourceType=4 OR Matching Game GP prefix)
			Where("activity_issues.activity_id IN ?", activityIDs).
			Group("activity_issues.activity_id").
			Scan(&gamePassStats)

		for _, s := range gamePassStats {
			if item, ok := activityMap[s.ActivityID]; ok {
				// 次卡价值 = 次卡抽奖次数 * 活动单价
				item.TotalGamePassValue = s.GamePassDraws * activityPriceMap[s.ActivityID]
			}
		}

		// 6. 计算盈亏和比率
		// 公式: 盈亏 = (支付金额 + 优惠券抵扣 + 次卡价值) - 产品成本
		finalList := make([]activityProfitLossItem, 0, len(activities))
		for _, a := range activities {
			item := activityMap[a.ID]
			totalIncome := item.TotalRevenue + item.TotalDiscount + item.TotalGamePassValue
			item.Profit = totalIncome - item.TotalCost
			if totalIncome > 0 {
				item.ProfitRate = float64(item.Profit) / float64(totalIncome)
			}
			finalList = append(finalList, *item)
		}

		// 按请求的字段排序
		if needCustomSort {
			sort.Slice(finalList, func(i, j int) bool {
				switch req.SortBy {
				case "profit":
					return finalList[i].Profit > finalList[j].Profit
				case "profit_asc":
					return finalList[i].Profit < finalList[j].Profit
				case "profit_rate":
					return finalList[i].ProfitRate > finalList[j].ProfitRate
				case "draw_count":
					return finalList[i].DrawCount > finalList[j].DrawCount
				default:
					return false // 保持原有顺序 (id DESC)
				}
			})

			// 排序后再分页
			start := (req.Page - 1) * req.PageSize
			end := start + req.PageSize
			if start > len(finalList) {
				start = len(finalList)
			}
			if end > len(finalList) {
				end = len(finalList)
			}
			finalList = finalList[start:end]
		}

		ctx.Payload(&activityProfitLossResponse{
			Page:     req.Page,
			PageSize: req.PageSize,
			Total:    total,
			List:     finalList,
		})
	}
}

type activityLogsRequest struct {
	Page     int `form:"page"`
	PageSize int `form:"page_size"`
}

type activityLogItem struct {
	ID              int64     `json:"id"`
	UserID          int64     `json:"user_id"`
	Nickname        string    `json:"nickname"`
	Avatar          string    `json:"avatar"`
	ProductID       int64     `json:"product_id"`
	ProductName     string    `json:"product_name"`
	ProductImage    string    `json:"product_image"`
	ProductPrice    int64     `json:"product_price"`
	ProductQuantity int64     `json:"product_quantity"` // 奖品数量
	OrderAmount     int64     `json:"order_amount"`
	OrderNo         string    `json:"order_no"`        // 订单号
	DiscountAmount  int64     `json:"discount_amount"` // 优惠金额(分)
	PayType         string    `json:"pay_type"`        // 支付方式/类型 (现金/道具卡/次数卡)
	UsedCard        string    `json:"used_card"`       // 使用的卡券名称(兼容旧字段)
	OrderStatus     int32     `json:"order_status"`    // 订单状态: 1待支付 2已支付 3已取消 4已退款
	Profit          int64     `json:"profit"`
	CreatedAt       time.Time `json:"created_at"`
	// 新增：详细支付信息
	PaymentDetails PaymentDetails `json:"payment_details"`
}

// PaymentDetails 支付详细信息
type PaymentDetails struct {
	CouponUsed     bool   `json:"coupon_used"`     // 是否使用优惠券
	CouponName     string `json:"coupon_name"`     // 优惠券名称
	CouponDiscount int64  `json:"coupon_discount"` // 优惠券抵扣金额(分)
	ItemCardUsed   bool   `json:"item_card_used"`  // 是否使用道具卡
	ItemCardName   string `json:"item_card_name"`  // 道具卡名称
	GamePassUsed   bool   `json:"game_pass_used"`  // 是否使用次数卡
	GamePassInfo   string `json:"game_pass_info"`  // 次数卡使用信息
	PointsUsed     bool   `json:"points_used"`     // 是否使用积分
	PointsDiscount int64  `json:"points_discount"` // 积分抵扣金额(分)
}

type activityLogsResponse struct {
	Page     int               `json:"page"`
	PageSize int               `json:"page_size"`
	Total    int64             `json:"total"`
	List     []activityLogItem `json:"list"`
}

func (h *handler) DashboardActivityLogs() core.HandlerFunc {
	return func(ctx core.Context) {
		activityID, _ := strconv.ParseInt(ctx.Param("activity_id"), 10, 64)
		if activityID <= 0 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "Invalid activity ID"))
			return
		}

		req := new(activityLogsRequest)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		if req.Page <= 0 {
			req.Page = 1
		}
		if req.PageSize <= 0 {
			req.PageSize = 20
		}

		db := h.repo.GetDbR().WithContext(ctx.RequestContext())

		var total int64
		db.Table(model.TableNameActivityDrawLogs).
			Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
			Where("activity_issues.activity_id = ?", activityID).
			Count(&total)

		var logs []struct {
			ID             int64
			UserID         int64
			Nickname       string
			Avatar         string
			ProductID      int64
			ProductName    string
			ImagesJSON     string
			ProductPrice   int64
			OrderAmount    int64
			DiscountAmount int64
			PointsAmount   int64 // 积分抵扣金额
			OrderStatus    int32 // 订单状态
			SourceType     int32
			CouponID       int64
			CouponName     string
			ItemCardID     int64
			ItemCardName   string
			EffectType     int32
			Multiplier     int32
			OrderRemark    string // BUG修复：增加remark字段用于解析次数卡使用信息
			OrderNo        string // 订单号
			DrawCount      int64  // 该订单的总抽奖次数（用于金额分摊）
			UsedDrawLogID  int64  // 道具卡实际使用的日志ID
			CreatedAt      time.Time
		}

		err := db.Table(model.TableNameActivityDrawLogs).
			Select(`
				activity_draw_logs.id,
				activity_draw_logs.user_id,
				COALESCE(users.nickname, '') as nickname,
				COALESCE(users.avatar, '') as avatar,
				activity_reward_settings.product_id,
				COALESCE(products.name, '') as product_name,
				COALESCE(products.images_json, '[]') as images_json,
				COALESCE(products.price, 0) as product_price,
				COALESCE(orders.actual_amount, 0) as order_amount,
				COALESCE(orders.discount_amount, 0) as discount_amount,
				COALESCE(orders.points_amount, 0) as points_amount,
				COALESCE(orders.status, 0) as order_status,
				orders.source_type,
				COALESCE(orders.coupon_id, 0) as coupon_id,
				COALESCE(system_coupons.name, '') as coupon_name,
				COALESCE(orders.item_card_id, 0) as item_card_id,
				COALESCE(system_item_cards.name, '') as item_card_name,
				COALESCE(system_item_cards.effect_type, 0) as effect_type,
				COALESCE(system_item_cards.reward_multiplier_x1000, 0) as multiplier,
				COALESCE(orders.remark, '') as order_remark,
				COALESCE(orders.order_no, '') as order_no,
				COALESCE(order_draw_counts.draw_count, 1) as draw_count,
				COALESCE(user_item_cards.used_draw_log_id, 0) as used_draw_log_id,
				activity_draw_logs.created_at
			`).
			Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
			Joins("LEFT JOIN users ON users.id = activity_draw_logs.user_id").
			Joins("LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id").
			Joins("LEFT JOIN products ON products.id = activity_reward_settings.product_id").
			Joins("LEFT JOIN orders ON orders.id = activity_draw_logs.order_id").
			Joins("LEFT JOIN user_coupons ON user_coupons.id = orders.coupon_id").
			Joins("LEFT JOIN system_coupons ON system_coupons.id = user_coupons.coupon_id").
			Joins("LEFT JOIN user_item_cards ON user_item_cards.id = orders.item_card_id").
			Joins("LEFT JOIN system_item_cards ON system_item_cards.id = user_item_cards.card_id").
			Joins("LEFT JOIN (SELECT order_id, COUNT(*) as draw_count FROM activity_draw_logs GROUP BY order_id) as order_draw_counts ON order_draw_counts.order_id = activity_draw_logs.order_id").
			Where("activity_issues.activity_id = ?", activityID).
			Order("activity_draw_logs.id DESC").
			Offset((req.Page - 1) * req.PageSize).
			Limit(req.PageSize).
			Scan(&logs).Error

		if err != nil {
			h.logger.Error(fmt.Sprintf("GetActivityLogs error: %v", err))
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21022, err.Error()))
			return
		}

		list := make([]activityLogItem, len(logs))
		for i, l := range logs {
			var images []string
			_ = json.Unmarshal([]byte(l.ImagesJSON), &images)
			productImage := ""
			if len(images) > 0 {
				productImage = images[0]
			}

			// Default quantity is 1
			quantity := int64(1)

			// Determine PayType and UsedCard + PaymentDetails
			payType := "现金支付"
			usedCard := ""
			paymentDetails := PaymentDetails{} // 金额将在 drawCount 计算后设置

			// 检查是否使用了优惠券
			if l.CouponID > 0 || l.CouponName != "" {
				paymentDetails.CouponUsed = true
				paymentDetails.CouponName = l.CouponName
				if paymentDetails.CouponName == "" {
					paymentDetails.CouponName = "优惠券"
				}
				usedCard = paymentDetails.CouponName
				payType = "优惠券"
			}

			// 检查是否使用了道具卡
			// BUG FIX: 仅当该条日志的 ID 等于 item_card 记录的 used_draw_log_id 时，才显示道具卡信息
			// 防止一个订单下的所有抽奖记录都显示 "双倍快乐水"
			isCardValidForThisLog := (l.UsedDrawLogID == 0) || (l.UsedDrawLogID == l.ID)

			if (l.ItemCardID > 0 || l.ItemCardName != "") && isCardValidForThisLog {
				paymentDetails.ItemCardUsed = true
				paymentDetails.ItemCardName = l.ItemCardName
				if paymentDetails.ItemCardName == "" {
					paymentDetails.ItemCardName = "道具卡"
				}
				if usedCard != "" {
					usedCard = usedCard + " + " + paymentDetails.ItemCardName
				} else {
					usedCard = paymentDetails.ItemCardName
				}
				payType = "道具卡"

				// 计算双倍/多倍卡数量
				if l.EffectType == 1 && l.Multiplier > 1000 {
					quantity = quantity * int64(l.Multiplier) / 1000
				}
			}

			// 检查是否使用了次数卡 (source_type=4 或 remark包含use_game_pass)
			if l.SourceType == 4 || strings.Contains(l.OrderRemark, "use_game_pass") {
				paymentDetails.GamePassUsed = true
				// 解析 gp_use:ID:Count 格式获取次数卡使用信息
				gamePassInfo := "次数卡"
				if strings.Contains(l.OrderRemark, "gp_use:") {
					// 从remark中提取次数卡信息，格式: use_game_pass;gp_use:ID:Count;gp_use:ID:Count
					parts := strings.Split(l.OrderRemark, ";")
					var gpParts []string
					for _, p := range parts {
						if strings.HasPrefix(p, "gp_use:") {
							gpParts = append(gpParts, p)
						}
					}
					if len(gpParts) > 0 {
						gamePassInfo = fmt.Sprintf("使用%d种次数卡", len(gpParts))
					}
				}
				paymentDetails.GamePassInfo = gamePassInfo
				if usedCard != "" {
					usedCard = usedCard + " + " + gamePassInfo
				} else {
					usedCard = gamePassInfo
				}
				payType = "次数卡"
			}

			// 检查是否使用了积分
			if l.PointsAmount > 0 {
				paymentDetails.PointsUsed = true
			}

			// 如果同时使用了多种方式，标记为组合支付
			usedCount := 0
			if paymentDetails.CouponUsed {
				usedCount++
			}
			if paymentDetails.ItemCardUsed {
				usedCount++
			}
			if paymentDetails.GamePassUsed {
				usedCount++
			}
			if usedCount > 1 {
				payType = "组合支付"
			} else if usedCount == 0 && l.OrderAmount > 0 {
				payType = "现金支付"
			} else if usedCount == 0 && l.OrderAmount == 0 {
				// 0元支付默认视为次数卡使用（实际业务中几乎不存在真正免费的情况）
				payType = "次数卡"
				paymentDetails.GamePassUsed = true
				if paymentDetails.GamePassInfo == "" {
					paymentDetails.GamePassInfo = "次数卡"
				}
			}

			// 计算单次抽奖的分摊金额（一个订单可能包含多次抽奖）
			drawCount := l.DrawCount
			if drawCount <= 0 {
				drawCount = 1
			}
			perDrawOrderAmount := l.OrderAmount / drawCount
			perDrawDiscountAmount := l.DiscountAmount / drawCount
			perDrawPointsAmount := l.PointsAmount / drawCount

			// 设置支付详情中的分摊金额
			paymentDetails.CouponDiscount = perDrawDiscountAmount
			paymentDetails.PointsDiscount = perDrawPointsAmount

			list[i] = activityLogItem{
				ID:              l.ID,
				UserID:          l.UserID,
				Nickname:        l.Nickname,
				Avatar:          l.Avatar,
				ProductID:       l.ProductID,
				ProductName:     l.ProductName,
				ProductImage:    productImage,
				ProductPrice:    l.ProductPrice,
				ProductQuantity: quantity,
				OrderAmount:     perDrawOrderAmount,    // 单次抽奖分摊的支付金额
				OrderNo:         l.OrderNo,             // 订单号
				DiscountAmount:  perDrawDiscountAmount, // 单次抽奖分摊的优惠金额
				PayType:         payType,
				UsedCard:        usedCard,
				OrderStatus:     l.OrderStatus,
				Profit:          perDrawOrderAmount + perDrawDiscountAmount - l.ProductPrice*quantity, // 单次盈亏 = 分摊收入 - 成本*数量
				CreatedAt:       l.CreatedAt,
				PaymentDetails:  paymentDetails,
			}
		}

		ctx.Payload(&activityLogsResponse{
			Page:     req.Page,
			PageSize: req.PageSize,
			Total:    total,
			List:     list,
		})
	}
}

type ensureActivityProfitLossMenuResponse struct {
	Ensured bool  `json:"ensured"`
	Parent  int64 `json:"parent_id"`
	MenuID  int64 `json:"menu_id"`
}

// EnsureActivityProfitLossMenu 确保运营分析下存在“活动盈亏”菜单
func (h *handler) EnsureActivityProfitLossMenu() core.HandlerFunc {
	return func(ctx core.Context) {
		// 1. 查找是否存在“控制台”或者“运营中心”类的父菜单
		// 很多系统会将概览放在 Dashboard 下。根据 titles_seed.go，运营是 Operations。
		parent, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Operations")).First()
		var parentID int64
		if parent == nil {
			// 如果没有 Operations，尝试查找 Dashboard
			parent, _ = h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Dashboard")).First()
		}

		if parent != nil {
			parentID = parent.ID
		}

		// 2. 查找活动盈亏菜单
		// 路径指向控制台并带上查参数
		menuPath := "/dashboard/console?tab=activity-profit"
		exists, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq(menuPath)).First()
		if exists != nil {
			ctx.Payload(&ensureActivityProfitLossMenuResponse{Ensured: true, Parent: parentID, MenuID: exists.ID})
			return
		}

		// 3. 创建菜单
		newMenu := &model.Menus{
			ParentID:    parentID,
			Path:        menuPath,
			Name:        "活动盈亏",
			Component:   "/dashboard/console/index",
			Icon:        "ri:pie-chart-2-fill",
			Sort:        60, // 排序在称号之后
			Status:      true,
			KeepAlive:   true,
			IsHide:      false,
			IsHideTab:   false,
			CreatedUser: "system",
			UpdatedUser: "system",
		}

		if err := h.writeDB.Menus.WithContext(ctx.RequestContext()).Create(newMenu); err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21023, "创建菜单失败: "+err.Error()))
			return
		}

		// 读取新创建的 ID
		created, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq(menuPath)).First()
		menuID := int64(0)
		if created != nil {
			menuID = created.ID
		}

		ctx.Payload(&ensureActivityProfitLossMenuResponse{Ensured: true, Parent: parentID, MenuID: menuID})
	}
}
