package admin

import (
	"net/http"
	"strconv"
	"time"

	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/validation"
)

type userProfitLossRequest struct {
	RangeType   string `form:"rangeType"`
	Granularity string `form:"granularity"`
}

type userProfitLossPoint struct {
	Date      string  `json:"date"`
	Cost      int64   `json:"cost"`   // 累计投入（已支付-已退款）
	Value     int64   `json:"value"`  // 累计产出（当前资产快照）
	Profit    int64   `json:"profit"` // 净盈亏
	Ratio     float64 `json:"ratio"`  // 盈亏比
	Breakdown struct {
		Products int64 `json:"products"`
		Points   int64 `json:"points"`
		Cards    int64 `json:"cards"`
		Coupons  int64 `json:"coupons"`
	} `json:"breakdown"`
}

type userProfitLossResponse struct {
	Granularity string                `json:"granularity"`
	List        []userProfitLossPoint `json:"list"`
	Summary     struct {
		TotalCost   int64   `json:"total_cost"`
		TotalValue  int64   `json:"total_value"`
		TotalProfit int64   `json:"total_profit"`
		AvgRatio    float64 `json:"avg_ratio"`
	} `json:"summary"`
	CurrentAssets struct {
		Points   int64 `json:"points"`
		Products int64 `json:"products"`
		Cards    int64 `json:"cards"`
		Coupons  int64 `json:"coupons"`
		Total    int64 `json:"total"`
	} `json:"currentAssets"`
}

func (h *handler) GetUserProfitLossTrend() core.HandlerFunc {
	return func(ctx core.Context) {
		userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
			return
		}

		req := new(userProfitLossRequest)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}

		start, end := parseRange(req.RangeType, "", "")
		if req.RangeType == "all" {
			u, _ := h.readDB.Users.WithContext(ctx.RequestContext()).Where(h.readDB.Users.ID.Eq(userID)).First()
			if u != nil {
				start = u.CreatedAt
			} else {
				start = time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local)
			}
		}
		gran := normalizeGranularity(req.Granularity)
		buckets := buildBuckets(start, end, gran)

		if len(buckets) > 1500 {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "范围过大"))
			return
		}

		// --- 1. 获取当前资产快照（实时余额）---
		var curAssets struct {
			Points   int64
			Products int64
			Cards    int64
			Coupons  int64
		}
		_ = h.repo.GetDbR().Raw("SELECT COALESCE(SUM(points), 0) FROM user_points WHERE user_id = ? AND (valid_end IS NULL OR valid_end > NOW())", userID).Scan(&curAssets.Points).Error
		_ = h.repo.GetDbR().Raw("SELECT COALESCE(SUM(p.price), 0) FROM user_inventory ui LEFT JOIN products p ON p.id = ui.product_id WHERE ui.user_id = ? AND ui.status = 1", userID).Scan(&curAssets.Products).Error
		_ = h.repo.GetDbR().Raw("SELECT COALESCE(SUM(sc.price), 0) FROM user_item_cards uic LEFT JOIN system_item_cards sc ON sc.id = uic.card_id WHERE uic.user_id = ? AND uic.status = 1", userID).Scan(&curAssets.Cards).Error
		_ = h.repo.GetDbR().Raw("SELECT COALESCE(SUM(balance_amount), 0) FROM user_coupons WHERE user_id = ? AND status = 1", userID).Scan(&curAssets.Coupons).Error
		totalAssetValue := curAssets.Points + curAssets.Products + curAssets.Cards + curAssets.Coupons

		// --- 2. 获取订单数据（仅 status=2 已支付） ---
		// 注意：为了计算累计趋势，我们需要获取 start 之前的所有已支付订单总额作为基数
		var baseCost int64 = 0
		var baseCostPtr *int64
		h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
			Select(h.readDB.Orders.ActualAmount.Sum()).
			Where(h.readDB.Orders.UserID.Eq(userID)).
			Where(h.readDB.Orders.Status.Eq(2)).
			Where(h.readDB.Orders.SourceType.In(2, 3, 4)). // 排除积分兑换(1)和奖品兑换(5)
			Where(h.readDB.Orders.CreatedAt.Lt(start)).
			Scan(&baseCostPtr)
		if baseCostPtr != nil {
			baseCost = *baseCostPtr
		}

		// 扣除历史退款 (如果有的话，此处简化处理，主要关注当前范围内的波动)
		var baseRefund int64 = 0
		_ = h.repo.GetDbR().Raw(`
			SELECT COALESCE(SUM(pr.amount_refund), 0) 
			FROM payment_refunds pr
			JOIN orders o ON o.order_no = pr.order_no COLLATE utf8mb4_unicode_ci
			WHERE o.user_id = ? AND pr.status = 'SUCCESS' AND pr.created_at < ?
		`, userID, start).Scan(&baseRefund).Error
		baseCost -= baseRefund
		if baseCost < 0 {
			baseCost = 0
		}

		orderRows, _ := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
			Where(h.readDB.Orders.UserID.Eq(userID)).
			Where(h.readDB.Orders.Status.Eq(2)).
			Where(h.readDB.Orders.SourceType.In(2, 3, 4)). // 排除积分兑换(1)和奖品兑换(5)
			Where(h.readDB.Orders.CreatedAt.Gte(start)).
			Where(h.readDB.Orders.CreatedAt.Lte(end)).
			Find()

		// 获取当前范围内的退款
		type refundInfo struct {
			Amount    int64
			CreatedAt time.Time
		}
		var refunds []refundInfo
		_ = h.repo.GetDbR().Raw(`
			SELECT pr.amount_refund as amount, pr.created_at
			FROM payment_refunds pr
			JOIN orders o ON o.order_no = pr.order_no COLLATE utf8mb4_unicode_ci
			WHERE o.user_id = ? AND pr.status = 'SUCCESS' AND pr.created_at BETWEEN ? AND ?
		`, userID, start, end).Scan(&refunds).Error

		// --- 3. 按时间分桶计算 ---
		list := make([]userProfitLossPoint, len(buckets))

		inBucket := func(t time.Time, b bucket) bool {
			return (t.After(b.Start) || t.Equal(b.Start)) && t.Before(b.End)
		}

		cumulativeCost := baseCost

		for i, b := range buckets {
			p := &list[i]
			p.Date = b.Label

			// 计算该时间段内的净投入变化
			var periodDelta int64 = 0
			for _, o := range orderRows {
				if inBucket(o.CreatedAt, b) {
					periodDelta += o.ActualAmount
				}
			}
			for _, r := range refunds {
				if inBucket(r.CreatedAt, b) {
					periodDelta -= r.Amount
				}
			}

			cumulativeCost += periodDelta
			if cumulativeCost < 0 {
				cumulativeCost = 0
			}
			p.Cost = cumulativeCost

			// 产出值：当前资产是一个存量值。
			// 理想逻辑是回溯各时间点的余额，简化逻辑下：
			// 如果该点还没有在该范围内发生过任何投入（且没有基数），则显示0；否则显示当前快照值。
			// 这里我们统一显示当前快照，但在前端图表上它会是一条水平线或阶梯线。
			p.Value = totalAssetValue
			p.Breakdown.Points = curAssets.Points
			p.Breakdown.Products = curAssets.Products
			p.Breakdown.Cards = curAssets.Cards
			p.Breakdown.Coupons = curAssets.Coupons

			p.Profit = p.Value - p.Cost
			if p.Cost > 0 {
				p.Ratio = float64(p.Value) / float64(p.Cost)
			} else if p.Value > 0 {
				p.Ratio = 99.9
			}
		}

		// 汇总数据
		var totalCost int64 = 0
		var totalCostPtr *int64
		h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
			Select(h.readDB.Orders.ActualAmount.Sum()).
			Where(h.readDB.Orders.UserID.Eq(userID)).
			Where(h.readDB.Orders.Status.Eq(2)).
			Where(h.readDB.Orders.SourceType.In(2, 3, 4)). // 排除积分兑换(1)和奖品兑换(5)
			Scan(&totalCostPtr)
		if totalCostPtr != nil {
			totalCost = *totalCostPtr
		}

		var totalRefund int64 = 0
		_ = h.repo.GetDbR().Raw(`
			SELECT COALESCE(SUM(pr.amount_refund), 0) 
			FROM payment_refunds pr
			JOIN orders o ON o.order_no = pr.order_no COLLATE utf8mb4_unicode_ci
			WHERE o.user_id = ? AND pr.status = 'SUCCESS'
		`, userID).Scan(&totalRefund).Error

		finalNetCost := totalCost - totalRefund
		if finalNetCost < 0 {
			finalNetCost = 0
		}

		resp := userProfitLossResponse{
			Granularity: gran,
			List:        list,
		}
		resp.Summary.TotalCost = finalNetCost
		resp.Summary.TotalValue = totalAssetValue
		resp.Summary.TotalProfit = totalAssetValue - finalNetCost
		if finalNetCost > 0 {
			resp.Summary.AvgRatio = float64(totalAssetValue) / float64(finalNetCost)
		} else if totalAssetValue > 0 {
			resp.Summary.AvgRatio = 99.9
		}

		resp.CurrentAssets.Points = curAssets.Points
		resp.CurrentAssets.Products = curAssets.Products
		resp.CurrentAssets.Cards = curAssets.Cards
		resp.CurrentAssets.Coupons = curAssets.Coupons
		resp.CurrentAssets.Total = totalAssetValue

		ctx.Payload(resp)
	}
}

// 盈亏明细请求
type profitLossDetailsRequest struct {
	Page      int    `form:"page"`
	PageSize  int    `form:"page_size"`
	RangeType string `form:"rangeType"`
}

// 盈亏明细项
type profitLossDetailItem struct {
	OrderID         int64  `json:"order_id"`
	OrderNo         string `json:"order_no"`
	CreatedAt       string `json:"created_at"`
	SourceType      int32  `json:"source_type"`       // 来源类型 1商城 2抽奖 3系统
	ActivityName    string `json:"activity_name"`     // 活动名称
	ActualAmount    int64  `json:"actual_amount"`     // 实际支付金额(分)
	RefundAmount    int64  `json:"refund_amount"`     // 退款金额(分)
	NetCost         int64  `json:"net_cost"`          // 净投入(分)
	PrizeValue      int64  `json:"prize_value"`       // 获得奖品价值(分)
	PrizeName       string `json:"prize_name"`        // 奖品名称
	PointsEarned    int64  `json:"points_earned"`     // 获得积分
	PointsValue     int64  `json:"points_value"`      // 积分价值(分)
	CouponUsedValue int64  `json:"coupon_used_value"` // 使用优惠券价值(分)
	CouponUsedName  string `json:"coupon_used_name"`  // 使用的优惠券名称
	ItemCardUsed    string `json:"item_card_used"`    // 使用的道具卡名称
	ItemCardValue   int64  `json:"item_card_value"`   // 道具卡价值(分)
	NetProfit       int64  `json:"net_profit"`        // 净盈亏
}

// 盈亏明细响应
type profitLossDetailsResponse struct {
	Page     int                    `json:"page"`
	PageSize int                    `json:"page_size"`
	Total    int64                  `json:"total"`
	List     []profitLossDetailItem `json:"list"`
	Summary  struct {
		TotalCost   int64 `json:"total_cost"`
		TotalValue  int64 `json:"total_value"`
		TotalProfit int64 `json:"total_profit"`
	} `json:"summary"`
}

// GetUserProfitLossDetails 获取用户盈亏明细
// @Summary 获取用户盈亏明细
// @Description 获取用户每笔订单的详细盈亏信息
// @Tags 管理端.用户
// @Accept json
// @Produce json
// @Param user_id path integer true "用户ID"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(20)
// @Param rangeType query string false "时间范围" default("all")
// @Success 200 {object} profitLossDetailsResponse
// @Failure 400 {object} code.Failure
// @Router /api/admin/users/{user_id}/stats/profit_loss_details [get]
// @Security LoginVerifyToken
func (h *handler) GetUserProfitLossDetails() core.HandlerFunc {
	return func(ctx core.Context) {
		userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
			return
		}

		req := new(profitLossDetailsRequest)
		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
		}
		if req.PageSize > 100 {
			req.PageSize = 100
		}

		// 解析时间范围
		start, end := parseRange(req.RangeType, "", "")
		if req.RangeType == "all" {
			u, _ := h.readDB.Users.WithContext(ctx.RequestContext()).Where(h.readDB.Users.ID.Eq(userID)).First()
			if u != nil {
				start = u.CreatedAt
			} else {
				start = time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local)
			}
		}

		// 查询订单总数
		orderQ := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
			Where(h.readDB.Orders.UserID.Eq(userID)).
			Where(h.readDB.Orders.Status.Eq(2)).
			Where(h.readDB.Orders.CreatedAt.Gte(start)).
			Where(h.readDB.Orders.CreatedAt.Lte(end))

		total, err := orderQ.Count()
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error()))
			return
		}

		// 分页查询订单
		orders, err := orderQ.Order(h.readDB.Orders.CreatedAt.Desc()).
			Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find()
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error()))
			return
		}

		// 收集订单ID
		orderIDs := make([]int64, len(orders))
		orderNos := make([]string, len(orders))
		for i, o := range orders {
			orderIDs[i] = o.ID
			orderNos[i] = o.OrderNo
		}

		// 批量查询退款信息
		refundMap := make(map[string]int64)
		if len(orderNos) > 0 {
			type refundRow struct {
				OrderNo string
				Amount  int64
			}
			var refunds []refundRow
			_ = h.repo.GetDbR().Raw(`
				SELECT order_no, COALESCE(SUM(amount_refund), 0) as amount 
				FROM payment_refunds 
				WHERE order_no IN ? AND status = 'SUCCESS' 
				GROUP BY order_no
			`, orderNos).Scan(&refunds).Error
			for _, r := range refunds {
				refundMap[r.OrderNo] = r.Amount
			}
		}

		// 批量查询库存价值（获得的奖品）
		prizeValueMap := make(map[int64]int64)
		prizeNameMap := make(map[int64]string)
		if len(orderIDs) > 0 {
			type prizeRow struct {
				OrderID int64
				Value   int64
				Name    string
			}
			var prizes []prizeRow
			_ = h.repo.GetDbR().Raw(`
				SELECT ui.order_id, COALESCE(SUM(p.price), 0) as value, 
					   GROUP_CONCAT(p.name SEPARATOR ', ') as name
				FROM user_inventory ui 
				LEFT JOIN products p ON p.id = ui.product_id 
				WHERE ui.order_id IN ? 
				GROUP BY ui.order_id
			`, orderIDs).Scan(&prizes).Error
			for _, p := range prizes {
				prizeValueMap[p.OrderID] = p.Value
				prizeNameMap[p.OrderID] = p.Name
			}
		}

		// 批量查询使用的优惠券
		couponValueMap := make(map[int64]int64)
		couponNameMap := make(map[int64]string)
		if len(orderIDs) > 0 {
			type couponRow struct {
				OrderID int64
				Value   int64
				Name    string
			}
			var coupons []couponRow
			_ = h.repo.GetDbR().Raw(`
				SELECT ucu.order_id, COALESCE(SUM(ABS(ucu.change_amount)), 0) as value,
					   GROUP_CONCAT(DISTINCT sc.name SEPARATOR ', ') as name
				FROM user_coupon_usage ucu
				LEFT JOIN user_coupons uc ON uc.id = ucu.user_coupon_id
				LEFT JOIN system_coupons sc ON sc.id = uc.coupon_id
				WHERE ucu.order_id IN ?
				GROUP BY ucu.order_id
			`, orderIDs).Scan(&coupons).Error
			for _, c := range coupons {
				couponValueMap[c.OrderID] = c.Value
				couponNameMap[c.OrderID] = c.Name
			}
		}

		// 批量查询活动信息
		activityNameMap := make(map[int64]string)
		if len(orderIDs) > 0 {
			type actRow struct {
				OrderID      int64
				ActivityName string
			}
			var acts []actRow
			_ = h.repo.GetDbR().Raw(`
				SELECT o.id as order_id, a.name as activity_name
				FROM orders o
				LEFT JOIN activities a ON a.id = o.activity_id
				WHERE o.id IN ? AND o.activity_id > 0
			`, orderIDs).Scan(&acts).Error
			for _, a := range acts {
				activityNameMap[a.OrderID] = a.ActivityName
			}
		}

		// 组装明细数据
		list := make([]profitLossDetailItem, len(orders))
		var totalCost, totalValue int64

		for i, o := range orders {
			refund := refundMap[o.OrderNo]
			prizeValue := prizeValueMap[o.ID]
			couponValue := couponValueMap[o.ID]
			netCost := o.ActualAmount - refund
			netProfit := prizeValue - netCost

			list[i] = profitLossDetailItem{
				OrderID:         o.ID,
				OrderNo:         o.OrderNo,
				CreatedAt:       o.CreatedAt.Format("2006-01-02 15:04:05"),
				SourceType:      o.SourceType,
				ActivityName:    activityNameMap[o.ID],
				ActualAmount:    o.ActualAmount,
				RefundAmount:    refund,
				NetCost:         netCost,
				PrizeValue:      prizeValue,
				PrizeName:       prizeNameMap[o.ID],
				PointsEarned:    0, // 简化处理
				PointsValue:     0,
				CouponUsedValue: couponValue,
				CouponUsedName:  couponNameMap[o.ID],
				ItemCardUsed:    "", // 从订单备注中解析
				ItemCardValue:   0,
				NetProfit:       netProfit,
			}

			// 解析道具卡信息（从订单备注）
			if o.Remark != "" {
				list[i].ItemCardUsed = parseItemCardFromRemark(o.Remark)
			}

			totalCost += netCost
			totalValue += prizeValue
		}

		resp := profitLossDetailsResponse{
			Page:     req.Page,
			PageSize: req.PageSize,
			Total:    total,
			List:     list,
		}
		resp.Summary.TotalCost = totalCost
		resp.Summary.TotalValue = totalValue
		resp.Summary.TotalProfit = totalValue - totalCost

		ctx.Payload(resp)
	}
}

// 从订单备注中解析道具卡信息
func parseItemCardFromRemark(remark string) string {
	// 格式: itemcard:xxx|...
	if len(remark) == 0 {
		return ""
	}
	idx := 0
	for i := 0; i < len(remark); i++ {
		if remark[i:] == "itemcard:" || (i+9 <= len(remark) && remark[i:i+9] == "itemcard:") {
			idx = i
			break
		}
	}
	if idx == 0 && len(remark) < 9 {
		return ""
	}
	if idx+9 >= len(remark) {
		return ""
	}
	seg := remark[idx+9:]
	// 找到 | 分隔符
	end := len(seg)
	for i := 0; i < len(seg); i++ {
		if seg[i] == '|' {
			end = i
			break
		}
	}
	return seg[:end]
}
