package app

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"bindbox-game/internal/code"
	"bindbox-game/internal/pkg/core"
	"bindbox-game/internal/pkg/validation"
	"bindbox-game/internal/repository/mysql/model"
)

type listDrawLogsRequest struct {
	Page     int    `form:"page"`
	PageSize int    `form:"page_size"`
	Level    *int32 `form:"level"`
}

type drawLogItem struct {
	ID           int64     `json:"id"`
	UserID       int64     `json:"user_id"`
	UserName     string    `json:"user_name"`
	Avatar       string    `json:"avatar"`
	IssueID      int64     `json:"issue_id"`
	OrderID      int64     `json:"order_id"`
	RewardID     int64     `json:"reward_id"`
	RewardName   string    `json:"reward_name"`
	RewardImage  string    `json:"reward_image"`
	IsWinner     int32     `json:"is_winner"`
	Level        int32     `json:"level"`
	CurrentLevel int32     `json:"current_level"`
	CreatedAt    time.Time `json:"created_at"`
}

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

// ListDrawLogs 抽奖记录列表
// @Summary 抽奖记录列表
// @Description 查看指定活动期数的抽奖记录，支持等级筛选（默认返回最新的100条，不支持自定义翻页）
// @Tags APP端.活动
// @Accept json
// @Produce json
// @Param activity_id path integer true "活动ID"
// @Param issue_id path integer true "期ID"
// @Param level query int false "奖品等级过滤"
// @Success 200 {object} listDrawLogsResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/activities/{activity_id}/issues/{issue_id}/draw_logs [get]
func (h *handler) ListDrawLogs() core.HandlerFunc {
	return func(ctx core.Context) {
		req := new(listDrawLogsRequest)
		res := new(listDrawLogsResponse)
		if err := ctx.ShouldBindForm(req); err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
			return
		}
		issueID, err := strconv.ParseInt(ctx.Param("issue_id"), 10, 64)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递期ID"))
			return
		}

		page := req.Page
		if page <= 0 {
			page = 1
		}
		pageSize := req.PageSize
		if pageSize <= 0 {
			pageSize = 100
		}

		now := time.Now()
		// 计算5分钟前的时间点 (用于延迟显示)
		fiveMinutesAgo := now.Add(-5 * time.Minute)
		// 计算当天零点 (用于仅显示当天数据)
		startOfToday := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())

		// [修改] 强制获取当天最新的 100 条数据 (Service 层限制最大 100)
		// 忽略前端传入的 Page/PageSize，总是获取第一页的 100 条
		fetchPageSize := 100
		fetchPage := 1

		items, total, err := h.activity.ListDrawLogs(ctx.RequestContext(), issueID, fetchPage, fetchPageSize, req.Level)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ListDrawLogsError, err.Error()))
			return
		}

		// 收集ID用于批量查询
		var userIDs []int64
		var rewardIDs []int64
		userSet := make(map[int64]struct{})
		rewardSet := make(map[int64]struct{})

		var filteredItems []*model.ActivityDrawLogs
		for _, v := range items {
			// 1. 过滤掉太新的数据 (5分钟延迟)
			if v.CreatedAt.After(fiveMinutesAgo) {
				continue
			}
			// 2. 过滤掉非当天的数据 (当天零点之前)
			if v.CreatedAt.Before(startOfToday) {
				// 因为是按时间倒序返回的，一旦遇到早于今天的，后续的更早，直接结束
				break
			}
			// 3. 数量限制 (虽然 Service 取了 100，这里再保个底，或者遵循前端 pageSize?
			// 需求是 "获取当天的 最新100 个数据"，这里我们以 filteredItems 为准，
			// 如果前端 pageSize 传了比如 20，是否应该只给 20？
			// 按照通常逻辑，列表接口应遵循 pageSize。但在这种定制逻辑下，用户似乎想要的是“当天数据的视图”。
			// 保持原逻辑：遵循 pageSize 限制输出数量，但我们上面强行取了 100 作为源数据。
			// 如果用户原本想看 100 条，前端传 100 即可。
			if len(filteredItems) >= pageSize {
				break
			}
			filteredItems = append(filteredItems, v)

			if v.UserID > 0 {
				if _, ok := userSet[v.UserID]; !ok {
					userSet[v.UserID] = struct{}{}
					userIDs = append(userIDs, v.UserID)
				}
			}
			if v.RewardID > 0 {
				if _, ok := rewardSet[v.RewardID]; !ok {
					rewardSet[v.RewardID] = struct{}{}
					rewardIDs = append(rewardIDs, v.RewardID)
				}
			}
		}
		items = filteredItems
		total = int64(len(items))

		// 批量查询用户信息
		userNameMap := make(map[int64]string)
		userAvatarMap := make(map[int64]string)
		if len(userIDs) > 0 {
			users, err := h.readDB.Users.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Users.ID.In(userIDs...)).Find()
			if err == nil {
				for _, u := range users {
					userNameMap[u.ID] = u.Nickname
					userAvatarMap[u.ID] = u.Avatar
				}
			}
		}

		// 批量查询奖品与商品信息
		rewardNameMap := make(map[int64]string)
		rewardImageMap := make(map[int64]string)
		if len(rewardIDs) > 0 {
			rewards, err := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.ID.In(rewardIDs...)).Find()
			if err == nil {
				var productIDs []int64
				productSet := make(map[int64]struct{})
				rewardProductMap := make(map[int64]int64)

				for _, r := range rewards {
					// 不再使用 r.Name，只通过 ProductID 关联查询商品名称
					if r.ProductID > 0 {
						if _, ok := productSet[r.ProductID]; !ok {
							productSet[r.ProductID] = struct{}{}
							productIDs = append(productIDs, r.ProductID)
						}
						rewardProductMap[r.ID] = r.ProductID
					}
				}

				if len(productIDs) > 0 {
					products, err := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.ID.In(productIDs...)).Find()
					if err == nil {
						productImageMap := make(map[int64]string)
						productNameMap := make(map[int64]string)
						for _, p := range products {
							first := ""
							if p.ImagesJSON != "" {
								var arr []string
								_ = json.Unmarshal([]byte(p.ImagesJSON), &arr)
								if len(arr) > 0 {
									first = arr[0]
								}
							}
							productImageMap[p.ID] = first
							productNameMap[p.ID] = p.Name
						}
						// 填充奖品图片，优先使用商品名称
						for rid, pid := range rewardProductMap {
							rewardImageMap[rid] = productImageMap[pid]
							// 优先使用商品名称
							if pn, ok := productNameMap[pid]; ok && pn != "" {
								rewardNameMap[rid] = pn
							}
						}
					}
				}
			}
		}

		res.Page = page
		res.PageSize = pageSize
		res.Total = total
		res.List = make([]drawLogItem, len(items))
		for i, v := range items {
			res.List[i] = drawLogItem{
				ID:           v.ID,
				UserID:       v.UserID,
				UserName:     userNameMap[v.UserID],
				Avatar:       userAvatarMap[v.UserID],
				IssueID:      v.IssueID,
				OrderID:      v.OrderID,
				RewardID:     v.RewardID,
				RewardName:   rewardNameMap[v.RewardID],
				RewardImage:  rewardImageMap[v.RewardID],
				IsWinner:     v.IsWinner,
				Level:        v.Level,
				CurrentLevel: v.CurrentLevel,
				CreatedAt:    v.CreatedAt,
			}
		}
		ctx.Payload(res)
	}
}

// ListDrawLogsByLevel 按奖品等级分类的抽奖记录
// @Summary 按奖品等级分类的抽奖记录
// @Description 查看指定活动期数的抽奖记录，按奖品等级分组返回
// @Tags APP端.活动
// @Accept json
// @Produce json
// @Param activity_id path integer true "活动ID"
// @Param issue_id path integer true "期ID"
// @Success 200 {object} listDrawLogsByLevelResponse
// @Failure 400 {object} code.Failure
// @Router /api/app/activities/{activity_id}/issues/{issue_id}/draw_logs_grouped [get]
func (h *handler) ListDrawLogsByLevel() core.HandlerFunc {
	return func(ctx core.Context) {
		issueID, err := strconv.ParseInt(ctx.Param("issue_id"), 10, 64)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递期ID"))
			return
		}

		// 1. 获取所有中奖记录
		// 我们假设这里不需要分页，或者分页逻辑比较复杂（每个等级分页？）。
		// 根据需求描述“按奖品等级进行归类”，通常 implied 展示所有或者前N个。
		// 这里暂且获取所有（或者一个较大的限制），然后在内存中分组。
		// 如果数据量巨大，需要由 Service 层提供 Group By 查询。
		// 考虑到单期中奖人数通常有限（除非是大规模活动），先尝试获取列表后分组。
		logs, _, err := h.activity.ListDrawLogs(ctx.RequestContext(), issueID, 1, 1000, nil) // 假设最多显示1000条中奖记录
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ListDrawLogsError, err.Error()))
			return
		}

		// 2. 获取奖品配置以获取等级名称
		rewards, err := h.activity.ListIssueRewards(ctx.RequestContext(), issueID)
		if err != nil {
			ctx.AbortWithError(core.Error(http.StatusInternalServerError, code.GetActivityError, err.Error()))
			return
		}

		levelNameMap := make(map[int32]string)
		// 收集所有 ProductID 用于批量查询商品名称
		productIDs := make([]int64, 0, len(rewards))
		rewardProductMap := make(map[int64]int64) // rewardID -> productID
		for _, r := range rewards {
			if r.ProductID > 0 {
				productIDs = append(productIDs, r.ProductID)
				rewardProductMap[r.ID] = r.ProductID
			}
		}
		// 批量查询商品名称
		productNameMap := make(map[int64]string)
		if len(productIDs) > 0 {
			products, _ := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.ID.In(productIDs...)).Find()
			for _, p := range products {
				productNameMap[p.ID] = p.Name
			}
		}
		// 构建等级名称映射
		for _, r := range rewards {
			if _, ok := levelNameMap[r.Level]; !ok {
				// 使用商品名称作为等级名称
				if r.ProductID > 0 {
					if pn, ok := productNameMap[r.ProductID]; ok && pn != "" {
						levelNameMap[r.Level] = pn
					}
				}
				// 如果没有商品名称，使用等级编号
				if levelNameMap[r.Level] == "" {
					levelNameMap[r.Level] = fmt.Sprintf("等级%d", r.Level)
				}
			}
		}

		// 3. 分组 (恢复 5 分钟过滤)
		groupsMap := make(map[int32][]drawLogItem)
		fiveMinutesAgo := time.Now().Add(-5 * time.Minute)
		for _, v := range logs {
			if v.CreatedAt.After(fiveMinutesAgo) {
				continue
			}
			if v.IsWinner == 1 {
				item := drawLogItem{
					ID:           v.ID,
					UserID:       v.UserID,
					IssueID:      v.IssueID,
					OrderID:      v.OrderID,
					RewardID:     v.RewardID,
					IsWinner:     v.IsWinner,
					Level:        v.Level,
					CurrentLevel: v.CurrentLevel,
				}
				groupsMap[v.Level] = append(groupsMap[v.Level], item)
			}
		}

		// 4. 构造响应
		var resp listDrawLogsByLevelResponse
		for level, items := range groupsMap {
			group := drawLogGroup{
				Level:     level,
				LevelName: levelNameMap[level],
				List:      items,
			}
			resp.Groups = append(resp.Groups, group)
		}

		// 排序 Groups (Level 升序? 也就是 1等奖在前)
		// 简单的冒泡排序或 slice sort
		for i := 0; i < len(resp.Groups)-1; i++ {
			for j := 0; j < len(resp.Groups)-1-i; j++ {
				if resp.Groups[j].Level > resp.Groups[j+1].Level {
					resp.Groups[j], resp.Groups[j+1] = resp.Groups[j+1], resp.Groups[j]
				}
			}
		}

		ctx.Payload(resp)
	}
}

type drawLogGroup struct {
	Level     int32         `json:"level"`
	LevelName string        `json:"level_name"`
	List      []drawLogItem `json:"list"`
}

type listDrawLogsByLevelResponse struct {
	Groups []drawLogGroup `json:"groups"`
}
