package activityapi

import (
	"net/http"
	"strconv"

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

type cpWeeklyRankStatusData struct {
	Enabled         bool                     `json:"enabled"`
	Period          cpWeeklyRankPeriodData   `json:"period"`
	Rewards         []cpWeeklyRankRewardData `json:"rewards"`
	Entries         []cpWeeklyRankEntryData  `json:"entries"`
	PreviousEntries []cpWeeklyRankEntryData  `json:"previous_entries"`
	ServerTimeMS    int64                    `json:"server_time_ms"`
	RelationType    string                   `json:"relation_type"`
}

type cpWeeklyRankPeriodData struct {
	StartMS int64 `json:"start_ms"`
	EndMS   int64 `json:"end_ms"`
}

type cpWeeklyRankRewardData struct {
	RankNo          int32                        `json:"rank_no"`
	ResourceGroupID int64                        `json:"resource_group_id"`
	ResourceGroup   *checkinResourceGroupData    `json:"resource_group,omitempty"`
	Items           []cpWeeklyRankRewardItemData `json:"items"`
}

type cpWeeklyRankRewardItemData struct {
	ResourceType string `json:"resource_type"`
	DurationDays int64  `json:"duration_days"`
	DurationMS   int64  `json:"duration_ms"`
	Quantity     int64  `json:"quantity"`
	CoverURL     string `json:"cover_url"`
}

type cpWeeklyRankEntryData struct {
	Rank            int64                `json:"rank"`
	RelationshipID  string               `json:"relationship_id"`
	RelationType    string               `json:"relation_type"`
	Score           int64                `json:"score"`
	UserA           cpWeeklyRankUserData `json:"user_a"`
	UserB           cpWeeklyRankUserData `json:"user_b"`
	FormedAtMS      int64                `json:"formed_at_ms"`
	UpdatedAtMS     int64                `json:"updated_at_ms"`
	FirstScoredAtMS int64                `json:"first_scored_at_ms"`
	LastScoredAtMS  int64                `json:"last_scored_at_ms"`
}

type cpWeeklyRankUserData struct {
	UserID        int64  `json:"user_id,string"`
	DisplayUserID string `json:"display_user_id"`
	Username      string `json:"username"`
	Avatar        string `json:"avatar"`
}

func (h *Handler) getCPWeeklyRankStatus(writer http.ResponseWriter, request *http.Request) {
	if h.cpWeeklyRank == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.cpWeeklyRank.GetCPWeeklyRankStatus(request.Context(), &activityv1.GetCPWeeklyRankStatusRequest{
		Meta:  httpkit.ActivityMeta(request),
		Limit: int32(cpWeeklyRankLimit(request)),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	groupIDs := cpWeeklyRankRewardGroupIDs(resp.GetConfig())
	groups, ok := h.resolveCheckinResourceGroups(writer, request, groupIDs)
	if !ok {
		return
	}
	httpkit.WriteOK(writer, request, cpWeeklyRankStatusFromProto(resp, groups))
}

func cpWeeklyRankStatusFromProto(resp *activityv1.GetCPWeeklyRankStatusResponse, groups map[int64]checkinResourceGroupData) cpWeeklyRankStatusData {
	config := resp.GetConfig()
	return cpWeeklyRankStatusData{
		Enabled:         config.GetEnabled(),
		RelationType:    config.GetRelationType(),
		Period:          cpWeeklyRankPeriodData{StartMS: resp.GetPeriodStartMs(), EndMS: resp.GetPeriodEndMs()},
		Rewards:         cpWeeklyRankRewardsFromProto(config.GetRewards(), groups),
		Entries:         cpWeeklyRankEntriesFromProto(resp.GetEntries()),
		PreviousEntries: cpWeeklyRankEntriesFromProto(resp.GetPreviousEntries()),
		ServerTimeMS:    resp.GetServerTimeMs(),
	}
}

func cpWeeklyRankRewardsFromProto(items []*activityv1.CPWeeklyRankReward, groups map[int64]checkinResourceGroupData) []cpWeeklyRankRewardData {
	rewards := make([]cpWeeklyRankRewardData, 0, len(items))
	for _, item := range items {
		reward := cpWeeklyRankRewardData{
			RankNo:          item.GetRankNo(),
			ResourceGroupID: item.GetResourceGroupId(),
			Items:           []cpWeeklyRankRewardItemData{},
		}
		if group, ok := groups[item.GetResourceGroupId()]; ok {
			reward.ResourceGroup = &group
			reward.Items = cpWeeklyRankRewardItemsFromGroup(group)
		}
		rewards = append(rewards, reward)
	}
	return rewards
}

func cpWeeklyRankRewardItemsFromGroup(group checkinResourceGroupData) []cpWeeklyRankRewardItemData {
	items := make([]cpWeeklyRankRewardItemData, 0, len(group.Items))
	for _, item := range group.Items {
		reward := cpWeeklyRankRewardItemData{
			DurationMS: item.DurationMS,
			Quantity:   item.Quantity,
		}
		if item.DurationMS > 0 {
			reward.DurationDays = item.DurationMS / (24 * 60 * 60 * 1000)
		}
		if item.Resource != nil {
			reward.ResourceType = item.Resource.ResourceType
			reward.CoverURL = firstRechargeFirstNonEmpty(item.Resource.PreviewURL, item.Resource.AssetURL, item.Resource.AnimationURL)
		} else {
			reward.ResourceType = firstRechargeWalletAssetType(item.WalletAssetType)
			reward.Quantity = firstRechargePositiveOr(item.WalletAssetAmount, item.Quantity)
		}
		items = append(items, reward)
	}
	return items
}

func cpWeeklyRankEntriesFromProto(items []*activityv1.CPWeeklyRankEntry) []cpWeeklyRankEntryData {
	entries := make([]cpWeeklyRankEntryData, 0, len(items))
	for _, item := range items {
		entries = append(entries, cpWeeklyRankEntryData{
			Rank:            item.GetRank(),
			RelationshipID:  item.GetRelationshipId(),
			RelationType:    item.GetRelationType(),
			Score:           item.GetScore(),
			UserA:           cpWeeklyRankUserFromProto(item.GetUserA()),
			UserB:           cpWeeklyRankUserFromProto(item.GetUserB()),
			FormedAtMS:      item.GetFormedAtMs(),
			UpdatedAtMS:     item.GetUpdatedAtMs(),
			FirstScoredAtMS: item.GetFirstScoredAtMs(),
			LastScoredAtMS:  item.GetLastScoredAtMs(),
		})
	}
	return entries
}

func cpWeeklyRankUserFromProto(item *activityv1.CPWeeklyRankUser) cpWeeklyRankUserData {
	if item == nil {
		return cpWeeklyRankUserData{}
	}
	return cpWeeklyRankUserData{
		UserID:        item.GetUserId(),
		DisplayUserID: item.GetDisplayUserId(),
		Username:      item.GetUsername(),
		Avatar:        item.GetAvatar(),
	}
}

func cpWeeklyRankRewardGroupIDs(config *activityv1.CPWeeklyRankConfig) []int64 {
	if config == nil {
		return nil
	}
	ids := make([]int64, 0, len(config.GetRewards()))
	for _, reward := range config.GetRewards() {
		if reward.GetResourceGroupId() > 0 {
			ids = append(ids, reward.GetResourceGroupId())
		}
	}
	return ids
}

func cpWeeklyRankLimit(request *http.Request) int {
	value, _ := strconv.Atoi(request.URL.Query().Get("limit"))
	if value <= 0 {
		return 3
	}
	if value > 20 {
		return 20
	}
	return value
}
