package weeklystar

import (
	"strconv"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/activityclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	activityv1 "hyapp.local/api/proto/activity/v1"

	"github.com/gin-gonic/gin"
)

type Handler struct {
	activity activityclient.Client
	audit    shared.OperationLogger
}

func New(activity activityclient.Client, audit shared.OperationLogger) *Handler {
	return &Handler{activity: activity, audit: audit}
}

type cycleRequest struct {
	CycleID             string      `json:"cycle_id"`
	Title               string      `json:"title"`
	Status              string      `json:"status"`
	RegionID            int64       `json:"region_id"`
	StartMS             int64       `json:"start_ms"`
	EndMS               int64       `json:"end_ms"`
	GiftIDs             []string    `json:"gift_ids"`
	Gifts               []giftDTO   `json:"gifts"`
	Rewards             []rewardDTO `json:"rewards"`
	Top1ResourceGroupID int64       `json:"top1_resource_group_id"`
	Top2ResourceGroupID int64       `json:"top2_resource_group_id"`
	Top3ResourceGroupID int64       `json:"top3_resource_group_id"`
}

type statusRequest struct {
	Status string `json:"status"`
}

type cycleDTO struct {
	AppCode          string      `json:"app_code,omitempty"`
	CycleID          string      `json:"cycle_id"`
	ActivityCode     string      `json:"activity_code"`
	RegionID         int64       `json:"region_id"`
	Title            string      `json:"title"`
	Status           string      `json:"status"`
	StartMS          int64       `json:"start_ms"`
	EndMS            int64       `json:"end_ms"`
	Gifts            []giftDTO   `json:"gifts"`
	Rewards          []rewardDTO `json:"rewards"`
	CreatedByAdminID int64       `json:"created_by_admin_id"`
	UpdatedByAdminID int64       `json:"updated_by_admin_id"`
	SettledAtMS      int64       `json:"settled_at_ms"`
	CreatedAtMS      int64       `json:"created_at_ms"`
	UpdatedAtMS      int64       `json:"updated_at_ms"`
}

type giftDTO struct {
	GiftID    string `json:"gift_id"`
	SortOrder int32  `json:"sort_order"`
}

type rewardDTO struct {
	RankNo          int32 `json:"rank_no"`
	ResourceGroupID int64 `json:"resource_group_id"`
}

type leaderboardEntryDTO struct {
	RankNo          int32  `json:"rank_no"`
	UserID          int64  `json:"user_id,string"`
	Score           int64  `json:"score"`
	FirstScoredAtMS int64  `json:"first_scored_at_ms"`
	LastScoredAtMS  int64  `json:"last_scored_at_ms"`
	DisplayUserID   string `json:"display_user_id,omitempty"`
	Username        string `json:"username,omitempty"`
	Avatar          string `json:"avatar,omitempty"`
}

type settlementDTO struct {
	SettlementID    string `json:"settlement_id"`
	CycleID         string `json:"cycle_id"`
	RankNo          int32  `json:"rank_no"`
	UserID          int64  `json:"user_id,string"`
	Score           int64  `json:"score"`
	ResourceGroupID int64  `json:"resource_group_id"`
	WalletCommandID string `json:"wallet_command_id"`
	WalletGrantID   string `json:"wallet_grant_id"`
	Status          string `json:"status"`
	FailureReason   string `json:"failure_reason"`
	AttemptCount    int32  `json:"attempt_count"`
	CreatedAtMS     int64  `json:"created_at_ms"`
	UpdatedAtMS     int64  `json:"updated_at_ms"`
}

func (h *Handler) ListCycles(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.activity.ListWeeklyStarCycles(c.Request.Context(), &activityv1.ListWeeklyStarCyclesRequest{
		Meta:     h.meta(c),
		Status:   strings.TrimSpace(options.Status),
		StartMs:  parseInt64Query(c, "start_ms"),
		EndMs:    parseInt64Query(c, "end_ms"),
		RegionId: parseInt64Query(c, "region_id"),
		Page:     int32(options.Page),
		PageSize: int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取周星周期失败")
		return
	}
	items := make([]cycleDTO, 0, len(resp.GetCycles()))
	for _, cycle := range resp.GetCycles() {
		items = append(items, cycleFromProto(cycle))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) CreateCycle(c *gin.Context) {
	var req cycleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "周星周期参数不正确")
		return
	}
	resp, err := h.activity.CreateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{
		Meta:            h.meta(c),
		Cycle:           req.toProto(),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "create-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) GetCycle(c *gin.Context) {
	cycleID := strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.GetWeeklyStarCycle(c.Request.Context(), &activityv1.GetWeeklyStarCycleRequest{Meta: h.meta(c), CycleId: cycleID})
	if err != nil {
		response.ServerError(c, "获取周星周期详情失败")
		return
	}
	response.OK(c, cycleFromProto(resp.GetCycle()))
}

func (h *Handler) UpdateCycle(c *gin.Context) {
	var req cycleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "周星周期参数不正确")
		return
	}
	req.CycleID = strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.UpdateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{
		Meta:            h.meta(c),
		Cycle:           req.toProto(),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "update-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) SetCycleStatus(c *gin.Context) {
	var req statusRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "周星周期状态参数不正确")
		return
	}
	cycleID := strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.SetWeeklyStarCycleStatus(c.Request.Context(), &activityv1.SetWeeklyStarCycleStatusRequest{
		Meta:            h.meta(c),
		CycleId:         cycleID,
		Status:          strings.TrimSpace(req.Status),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "set-weekly-star-cycle-status", "weekly_star_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) ListLeaderboard(c *gin.Context) {
	resp, err := h.activity.ListWeeklyStarLeaderboard(c.Request.Context(), &activityv1.ListWeeklyStarLeaderboardRequest{
		Meta:      h.meta(c),
		CycleId:   strings.TrimSpace(c.Param("cycle_id")),
		PageSize:  int32(parseIntQuery(c, "page_size", 50)),
		PageToken: strings.TrimSpace(c.Query("page_token")),
	})
	if err != nil {
		response.ServerError(c, "获取周星排行榜失败")
		return
	}
	items := make([]leaderboardEntryDTO, 0, len(resp.GetEntries()))
	for _, entry := range resp.GetEntries() {
		items = append(items, leaderboardEntryFromProto(entry))
	}
	response.OK(c, gin.H{"cycle": cycleFromProto(resp.GetCycle()), "items": items, "total": resp.GetTotal(), "next_page_token": resp.GetNextPageToken()})
}

func (h *Handler) ListSettlements(c *gin.Context) {
	resp, err := h.activity.ListWeeklyStarSettlements(c.Request.Context(), &activityv1.ListWeeklyStarSettlementsRequest{
		Meta:    h.meta(c),
		CycleId: strings.TrimSpace(c.Param("cycle_id")),
	})
	if err != nil {
		response.ServerError(c, "获取周星结算记录失败")
		return
	}
	items := make([]settlementDTO, 0, len(resp.GetSettlements()))
	for _, settlement := range resp.GetSettlements() {
		items = append(items, settlementFromProto(settlement))
	}
	response.OK(c, items)
}

func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
	return &activityv1.RequestMeta{
		RequestId: middleware.CurrentRequestID(c),
		Caller:    "admin-server",
		AppCode:   appctx.FromContext(c.Request.Context()),
		SentAtMs:  time.Now().UnixMilli(),
	}
}

func (r cycleRequest) toProto() *activityv1.WeeklyStarCycle {
	gifts := make([]*activityv1.WeeklyStarGift, 0, 3)
	if len(r.Gifts) > 0 {
		for _, gift := range r.Gifts {
			gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(gift.GiftID), SortOrder: gift.SortOrder})
		}
	} else {
		for index, giftID := range r.GiftIDs {
			// 兼容旧 gift_ids 请求：数组首项是页面首项，因此在大值优先语义下分配最高默认权重。
			gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(giftID), SortOrder: int32(len(r.GiftIDs) - index)})
		}
	}
	rewards := make([]*activityv1.WeeklyStarReward, 0, 3)
	if len(r.Rewards) > 0 {
		for _, reward := range r.Rewards {
			rewards = append(rewards, &activityv1.WeeklyStarReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID})
		}
	} else {
		rewards = append(rewards,
			&activityv1.WeeklyStarReward{RankNo: 1, ResourceGroupId: r.Top1ResourceGroupID},
			&activityv1.WeeklyStarReward{RankNo: 2, ResourceGroupId: r.Top2ResourceGroupID},
			&activityv1.WeeklyStarReward{RankNo: 3, ResourceGroupId: r.Top3ResourceGroupID},
		)
	}
	return &activityv1.WeeklyStarCycle{
		CycleId:  strings.TrimSpace(r.CycleID),
		RegionId: r.RegionID,
		Title:    strings.TrimSpace(r.Title),
		Status:   strings.TrimSpace(r.Status),
		StartMs:  r.StartMS,
		EndMs:    r.EndMS,
		Gifts:    gifts,
		Rewards:  rewards,
	}
}

func cycleFromProto(cycle *activityv1.WeeklyStarCycle) cycleDTO {
	if cycle == nil {
		return cycleDTO{Gifts: []giftDTO{}, Rewards: []rewardDTO{}}
	}
	gifts := make([]giftDTO, 0, len(cycle.GetGifts()))
	for _, gift := range cycle.GetGifts() {
		gifts = append(gifts, giftDTO{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()})
	}
	rewards := make([]rewardDTO, 0, len(cycle.GetRewards()))
	for _, reward := range cycle.GetRewards() {
		rewards = append(rewards, rewardDTO{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()})
	}
	return cycleDTO{
		AppCode:          cycle.GetAppCode(),
		CycleID:          cycle.GetCycleId(),
		ActivityCode:     cycle.GetActivityCode(),
		RegionID:         cycle.GetRegionId(),
		Title:            cycle.GetTitle(),
		Status:           cycle.GetStatus(),
		StartMS:          cycle.GetStartMs(),
		EndMS:            cycle.GetEndMs(),
		Gifts:            gifts,
		Rewards:          rewards,
		CreatedByAdminID: cycle.GetCreatedByAdminId(),
		UpdatedByAdminID: cycle.GetUpdatedByAdminId(),
		SettledAtMS:      cycle.GetSettledAtMs(),
		CreatedAtMS:      cycle.GetCreatedAtMs(),
		UpdatedAtMS:      cycle.GetUpdatedAtMs(),
	}
}

func leaderboardEntryFromProto(entry *activityv1.WeeklyStarLeaderboardEntry) leaderboardEntryDTO {
	if entry == nil {
		return leaderboardEntryDTO{}
	}
	return leaderboardEntryDTO{
		RankNo:          entry.GetRankNo(),
		UserID:          entry.GetUserId(),
		Score:           entry.GetScore(),
		FirstScoredAtMS: entry.GetFirstScoredAtMs(),
		LastScoredAtMS:  entry.GetLastScoredAtMs(),
	}
}

func settlementFromProto(settlement *activityv1.WeeklyStarSettlement) settlementDTO {
	if settlement == nil {
		return settlementDTO{}
	}
	return settlementDTO{
		SettlementID:    settlement.GetSettlementId(),
		CycleID:         settlement.GetCycleId(),
		RankNo:          settlement.GetRankNo(),
		UserID:          settlement.GetUserId(),
		Score:           settlement.GetScore(),
		ResourceGroupID: settlement.GetResourceGroupId(),
		WalletCommandID: settlement.GetWalletCommandId(),
		WalletGrantID:   settlement.GetWalletGrantId(),
		Status:          settlement.GetStatus(),
		FailureReason:   settlement.GetFailureReason(),
		AttemptCount:    settlement.GetAttemptCount(),
		CreatedAtMS:     settlement.GetCreatedAtMs(),
		UpdatedAtMS:     settlement.GetUpdatedAtMs(),
	}
}

func parseInt64Query(c *gin.Context, key string) int64 {
	value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64)
	return value
}

func parseIntQuery(c *gin.Context, key string, fallback int) int {
	value, err := strconv.Atoi(strings.TrimSpace(c.Query(key)))
	if err != nil || value <= 0 {
		return fallback
	}
	return value
}
