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
		_ = 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.CreatedAt.Lt(start)).
			Scan(&baseCost)

		// 扣除历史退款 (如果有的话，此处简化处理，主要关注当前范围内的波动)
		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.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
		_ = 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)).
			Scan(&totalCost)

		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)
	}
}
