package activityapi

import (
	"net/http"
	"strings"

	activityv1 "hyapp.local/api/proto/activity/v1"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

type levelOverviewData struct {
	Tracks       []levelTrackOverviewData `json:"tracks"`
	ServerTimeMS int64                    `json:"server_time_ms"`
}

type levelTrackOverviewData struct {
	Track                        string                   `json:"track"`
	Name                         string                   `json:"name"`
	Level                        int32                    `json:"level"`
	TierID                       int64                    `json:"tier_id"`
	TotalValue                   int64                    `json:"total_value"`
	CurrentLevelRequiredValue    int64                    `json:"current_level_required_value"`
	NextLevel                    int32                    `json:"next_level"`
	NextLevelRequiredValue       int64                    `json:"next_level_required_value"`
	DisplayAvatarFrameResourceID int64                    `json:"display_avatar_frame_resource_id"`
	DisplayAvatarFrameResource   *achievementResourceData `json:"display_avatar_frame_resource,omitempty"`
	DisplayBadgeResourceID       int64                    `json:"display_badge_resource_id"`
	DisplayBadgeResource         *achievementResourceData `json:"display_badge_resource,omitempty"`
	DisplayBadgeSourceLevel      int32                    `json:"display_badge_source_level"`
	RewardPendingCount           int64                    `json:"reward_pending_count"`
	SortOrder                    int32                    `json:"sort_order"`
}

type levelTrackDetailData struct {
	Overview     levelTrackOverviewData `json:"overview"`
	Rules        []levelRuleData        `json:"rules"`
	Tiers        []levelTierData        `json:"tiers"`
	ServerTimeMS int64                  `json:"server_time_ms"`
}

type levelRuleData struct {
	Track                 string                        `json:"track"`
	Level                 int32                         `json:"level"`
	RequiredValue         int64                         `json:"required_value"`
	Name                  string                        `json:"name"`
	Status                string                        `json:"status"`
	RewardResourceGroupID int64                         `json:"reward_resource_group_id"`
	RewardResourceGroup   *achievementResourceGroupData `json:"reward_resource_group,omitempty"`
	SortOrder             int32                         `json:"sort_order"`
	DisplayConfigJSON     string                        `json:"display_config_json"`
	CreatedByAdminID      int64                         `json:"created_by_admin_id"`
	UpdatedByAdminID      int64                         `json:"updated_by_admin_id"`
	CreatedAtMS           int64                         `json:"created_at_ms"`
	UpdatedAtMS           int64                         `json:"updated_at_ms"`
}

type levelTierData struct {
	TierID                       int64                         `json:"tier_id"`
	Track                        string                        `json:"track"`
	MinLevel                     int32                         `json:"min_level"`
	MaxLevel                     int32                         `json:"max_level"`
	Name                         string                        `json:"name"`
	DisplayAvatarFrameResourceID int64                         `json:"display_avatar_frame_resource_id"`
	DisplayAvatarFrameResource   *achievementResourceData      `json:"display_avatar_frame_resource,omitempty"`
	DisplayBadgeResourceID       int64                         `json:"display_badge_resource_id"`
	DisplayBadgeResource         *achievementResourceData      `json:"display_badge_resource,omitempty"`
	RewardResourceGroupID        int64                         `json:"reward_resource_group_id"`
	RewardResourceGroup          *achievementResourceGroupData `json:"reward_resource_group,omitempty"`
	Status                       string                        `json:"status"`
	DisplayConfigJSON            string                        `json:"display_config_json"`
	CreatedByAdminID             int64                         `json:"created_by_admin_id"`
	UpdatedByAdminID             int64                         `json:"updated_by_admin_id"`
	CreatedAtMS                  int64                         `json:"created_at_ms"`
	UpdatedAtMS                  int64                         `json:"updated_at_ms"`
}

type levelRewardData struct {
	RewardJobID      string `json:"reward_job_id"`
	UserID           int64  `json:"user_id"`
	Track            string `json:"track"`
	RewardSourceType string `json:"reward_source_type"`
	RewardSourceID   string `json:"reward_source_id"`
	ResourceGroupID  int64  `json:"resource_group_id"`
	WalletCommandID  string `json:"wallet_command_id"`
	WalletGrantID    string `json:"wallet_grant_id"`
	Status           string `json:"status"`
	AttemptCount     int32  `json:"attempt_count"`
	FailureReason    string `json:"failure_reason"`
	CreatedAtMS      int64  `json:"created_at_ms"`
	UpdatedAtMS      int64  `json:"updated_at_ms"`
}

func (h *Handler) getMyLevelOverview(writer http.ResponseWriter, request *http.Request) {
	if h.growthLevelClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.growthLevelClient.GetMyLevelOverview(request.Context(), &activityv1.GetMyLevelOverviewRequest{
		Meta:   httpkit.ActivityMeta(request),
		UserId: auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	// activity-service 只拥有等级关系和资源 ID；素材 URL 属于 wallet-service。Gateway 在同一响应补齐当前等级素材，
	// 避免 H5 再依赖后台接口或把线上资源 ID 映射硬编码进页面。
	resources, ok := h.resolveAchievementResources(writer, request, levelOverviewResourceIDs(resp.GetTracks()))
	if !ok {
		return
	}
	tracks := make([]levelTrackOverviewData, 0, len(resp.GetTracks()))
	for _, item := range resp.GetTracks() {
		tracks = append(tracks, levelTrackOverviewFromProto(item, resources))
	}
	httpkit.WriteOK(writer, request, levelOverviewData{Tracks: tracks, ServerTimeMS: resp.GetServerTimeMs()})
}

func (h *Handler) getLevelTrack(writer http.ResponseWriter, request *http.Request) {
	if h.growthLevelClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	track := strings.TrimSpace(request.PathValue("track"))
	if track == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.growthLevelClient.GetLevelTrack(request.Context(), &activityv1.GetLevelTrackRequest{
		Meta:   httpkit.ActivityMeta(request),
		UserId: auth.UserIDFromContext(request.Context()),
		Track:  track,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	// 等级层页面必须一次拿到区间、徽章、头像框和升级礼物；这里按正 ID 去重后解析，
	// 既保持 activity/wallet 的 owner 边界，也避免前端为每一行产生额外请求。
	resources, ok := h.resolveAchievementResources(writer, request, levelTrackResourceIDs(resp.GetOverview(), resp.GetTiers()))
	if !ok {
		return
	}
	resourceGroups, ok := h.resolveAchievementResourceGroups(writer, request, levelTrackResourceGroupIDs(resp.GetRules(), resp.GetTiers()))
	if !ok {
		return
	}
	rules := make([]levelRuleData, 0, len(resp.GetRules()))
	for _, rule := range resp.GetRules() {
		rules = append(rules, levelRuleFromProto(rule, resourceGroups))
	}
	tiers := make([]levelTierData, 0, len(resp.GetTiers()))
	for _, tier := range resp.GetTiers() {
		tiers = append(tiers, levelTierFromProto(tier, resources, resourceGroups))
	}
	httpkit.WriteOK(writer, request, levelTrackDetailData{
		Overview:     levelTrackOverviewFromProto(resp.GetOverview(), resources),
		Rules:        rules,
		Tiers:        tiers,
		ServerTimeMS: resp.GetServerTimeMs(),
	})
}

func (h *Handler) listLevelRewards(writer http.ResponseWriter, request *http.Request) {
	if h.growthLevelClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	page, ok := httpkit.PositiveInt32Query(request, "page", 1)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 20)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.growthLevelClient.ListLevelRewards(request.Context(), &activityv1.ListLevelRewardsRequest{
		Meta:     httpkit.ActivityMeta(request),
		UserId:   auth.UserIDFromContext(request.Context()),
		Track:    strings.TrimSpace(request.URL.Query().Get("track")),
		Status:   strings.TrimSpace(request.URL.Query().Get("status")),
		Page:     page,
		PageSize: pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	rewards := make([]levelRewardData, 0, len(resp.GetRewards()))
	for _, reward := range resp.GetRewards() {
		rewards = append(rewards, levelRewardFromProto(reward))
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"rewards":   rewards,
		"total":     resp.GetTotal(),
		"page":      page,
		"page_size": pageSize,
	})
}

func levelTrackOverviewFromProto(item *activityv1.LevelTrackOverview, resources map[int64]achievementResourceData) levelTrackOverviewData {
	data := levelTrackOverviewData{
		Track:                        item.GetTrack(),
		Name:                         item.GetName(),
		Level:                        item.GetLevel(),
		TierID:                       item.GetTierId(),
		TotalValue:                   item.GetTotalValue(),
		CurrentLevelRequiredValue:    item.GetCurrentLevelRequiredValue(),
		NextLevel:                    item.GetNextLevel(),
		NextLevelRequiredValue:       item.GetNextLevelRequiredValue(),
		DisplayAvatarFrameResourceID: item.GetDisplayAvatarFrameResourceId(),
		DisplayBadgeResourceID:       item.GetDisplayBadgeResourceId(),
		DisplayBadgeSourceLevel:      item.GetDisplayBadgeSourceLevel(),
		RewardPendingCount:           item.GetRewardPendingCount(),
		SortOrder:                    item.GetSortOrder(),
	}
	if resource, ok := resources[item.GetDisplayAvatarFrameResourceId()]; ok {
		data.DisplayAvatarFrameResource = &resource
	}
	if resource, ok := resources[item.GetDisplayBadgeResourceId()]; ok {
		data.DisplayBadgeResource = &resource
	}
	return data
}

func levelRuleFromProto(item *activityv1.LevelRule, resourceGroups map[int64]achievementResourceGroupData) levelRuleData {
	data := levelRuleData{
		Track:                 item.GetTrack(),
		Level:                 item.GetLevel(),
		RequiredValue:         item.GetRequiredValue(),
		Name:                  item.GetName(),
		Status:                item.GetStatus(),
		RewardResourceGroupID: item.GetRewardResourceGroupId(),
		SortOrder:             item.GetSortOrder(),
		DisplayConfigJSON:     item.GetDisplayConfigJson(),
		CreatedByAdminID:      item.GetCreatedByAdminId(),
		UpdatedByAdminID:      item.GetUpdatedByAdminId(),
		CreatedAtMS:           item.GetCreatedAtMs(),
		UpdatedAtMS:           item.GetUpdatedAtMs(),
	}
	if group, ok := resourceGroups[item.GetRewardResourceGroupId()]; ok {
		data.RewardResourceGroup = &group
	}
	return data
}

func levelTierFromProto(item *activityv1.LevelTier, resources map[int64]achievementResourceData, resourceGroups map[int64]achievementResourceGroupData) levelTierData {
	data := levelTierData{
		TierID:                       item.GetTierId(),
		Track:                        item.GetTrack(),
		MinLevel:                     item.GetMinLevel(),
		MaxLevel:                     item.GetMaxLevel(),
		Name:                         item.GetName(),
		DisplayAvatarFrameResourceID: item.GetDisplayAvatarFrameResourceId(),
		DisplayBadgeResourceID:       item.GetDisplayBadgeResourceId(),
		RewardResourceGroupID:        item.GetRewardResourceGroupId(),
		Status:                       item.GetStatus(),
		DisplayConfigJSON:            item.GetDisplayConfigJson(),
		CreatedByAdminID:             item.GetCreatedByAdminId(),
		UpdatedByAdminID:             item.GetUpdatedByAdminId(),
		CreatedAtMS:                  item.GetCreatedAtMs(),
		UpdatedAtMS:                  item.GetUpdatedAtMs(),
	}
	if resource, ok := resources[item.GetDisplayAvatarFrameResourceId()]; ok {
		data.DisplayAvatarFrameResource = &resource
	}
	if resource, ok := resources[item.GetDisplayBadgeResourceId()]; ok {
		data.DisplayBadgeResource = &resource
	}
	if group, ok := resourceGroups[item.GetRewardResourceGroupId()]; ok {
		data.RewardResourceGroup = &group
	}
	return data
}

func levelOverviewResourceIDs(items []*activityv1.LevelTrackOverview) []int64 {
	ids := make([]int64, 0, len(items)*2)
	for _, item := range items {
		ids = append(ids, item.GetDisplayAvatarFrameResourceId(), item.GetDisplayBadgeResourceId())
	}
	return ids
}

func levelTrackResourceIDs(overview *activityv1.LevelTrackOverview, tiers []*activityv1.LevelTier) []int64 {
	ids := levelOverviewResourceIDs([]*activityv1.LevelTrackOverview{overview})
	for _, tier := range tiers {
		ids = append(ids, tier.GetDisplayAvatarFrameResourceId(), tier.GetDisplayBadgeResourceId())
	}
	return ids
}

func levelTrackResourceGroupIDs(rules []*activityv1.LevelRule, tiers []*activityv1.LevelTier) []int64 {
	ids := make([]int64, 0, len(rules)+len(tiers))
	for _, rule := range rules {
		ids = append(ids, rule.GetRewardResourceGroupId())
	}
	for _, tier := range tiers {
		ids = append(ids, tier.GetRewardResourceGroupId())
	}
	return ids
}

func levelRewardFromProto(item *activityv1.LevelRewardJob) levelRewardData {
	return levelRewardData{
		RewardJobID:      item.GetRewardJobId(),
		UserID:           item.GetUserId(),
		Track:            item.GetTrack(),
		RewardSourceType: item.GetRewardSourceType(),
		RewardSourceID:   item.GetRewardSourceId(),
		ResourceGroupID:  item.GetResourceGroupId(),
		WalletCommandID:  item.GetWalletCommandId(),
		WalletGrantID:    item.GetWalletGrantId(),
		Status:           item.GetStatus(),
		AttemptCount:     item.GetAttemptCount(),
		FailureReason:    item.GetFailureReason(),
		CreatedAtMS:      item.GetCreatedAtMs(),
		UpdatedAtMS:      item.GetUpdatedAtMs(),
	}
}
