package activityapi

import (
	"net/http"
	"strings"

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

type checkinStatusData struct {
	Enabled         bool                `json:"enabled"`
	CanSign         bool                `json:"can_sign"`
	Reason          string              `json:"reason"`
	CycleNo         int64               `json:"cycle_no"`
	CurrentDayIndex int32               `json:"current_day_index"`
	NextDayIndex    int32               `json:"next_day_index"`
	CheckinDay      string              `json:"checkin_day"`
	LastCheckinDay  string              `json:"last_checkin_day"`
	ConfigVersion   int64               `json:"config_version"`
	Rewards         []checkinRewardData `json:"rewards"`
	ServerTimeMS    int64               `json:"server_time_ms"`
	NextRefreshAtMS int64               `json:"next_refresh_at_ms"`
}

type checkinRewardData struct {
	DayIndex        int32                     `json:"day_index"`
	ResourceGroupID int64                     `json:"resource_group_id"`
	Status          string                    `json:"status"`
	ClaimID         string                    `json:"claim_id,omitempty"`
	WalletGrantID   string                    `json:"wallet_grant_id,omitempty"`
	GrantedAtMS     int64                     `json:"granted_at_ms,omitempty"`
	ResourceGroup   *checkinResourceGroupData `json:"resource_group,omitempty"`
}

type checkinSignData struct {
	ClaimID         string `json:"claim_id"`
	CycleNo         int64  `json:"cycle_no"`
	CheckinDay      string `json:"checkin_day"`
	DayIndex        int32  `json:"day_index"`
	ResourceGroupID int64  `json:"resource_group_id"`
	Status          string `json:"status"`
	WalletGrantID   string `json:"wallet_grant_id"`
	GrantedAtMS     int64  `json:"granted_at_ms"`
	ServerTimeMS    int64  `json:"server_time_ms"`
}

type checkinSignRequestBody struct {
	CommandID      string `json:"command_id"`
	CommandIDCamel string `json:"commandId"`
}

func (b checkinSignRequestBody) normalizedCommandID() string {
	if value := strings.TrimSpace(b.CommandID); value != "" {
		return value
	}
	return strings.TrimSpace(b.CommandIDCamel)
}

type checkinResourceData struct {
	ResourceID        int64    `json:"resource_id"`
	ResourceCode      string   `json:"resource_code"`
	ResourceType      string   `json:"resource_type"`
	Name              string   `json:"name"`
	Status            string   `json:"status"`
	Grantable         bool     `json:"grantable"`
	GrantStrategy     string   `json:"grant_strategy"`
	WalletAssetType   string   `json:"wallet_asset_type,omitempty"`
	WalletAssetAmount int64    `json:"wallet_asset_amount,omitempty"`
	UsageScopes       []string `json:"usage_scopes"`
	AssetURL          string   `json:"asset_url"`
	PreviewURL        string   `json:"preview_url"`
	AnimationURL      string   `json:"animation_url"`
	MetadataJSON      string   `json:"metadata_json"`
	SortOrder         int32    `json:"sort_order"`
	CreatedAtMS       int64    `json:"created_at_ms"`
	UpdatedAtMS       int64    `json:"updated_at_ms"`
}

type checkinResourceGroupItemData struct {
	GroupItemID       int64                `json:"group_item_id"`
	ItemType          string               `json:"item_type"`
	ResourceID        int64                `json:"resource_id"`
	Resource          *checkinResourceData `json:"resource,omitempty"`
	WalletAssetType   string               `json:"wallet_asset_type,omitempty"`
	WalletAssetAmount int64                `json:"wallet_asset_amount,omitempty"`
	Quantity          int64                `json:"quantity"`
	DurationMS        int64                `json:"duration_ms"`
	SortOrder         int32                `json:"sort_order"`
}

type checkinResourceGroupData struct {
	GroupID     int64                          `json:"group_id"`
	GroupCode   string                         `json:"group_code"`
	Name        string                         `json:"name"`
	Status      string                         `json:"status"`
	Description string                         `json:"description"`
	SortOrder   int32                          `json:"sort_order"`
	Items       []checkinResourceGroupItemData `json:"items"`
	CreatedAtMS int64                          `json:"created_at_ms"`
	UpdatedAtMS int64                          `json:"updated_at_ms"`
}

// getSevenDayCheckInStatus 返回当前用户七日签到面板；资源组展示材料由 gateway 从 wallet-service 补齐。
func (h *Handler) getSevenDayCheckInStatus(writer http.ResponseWriter, request *http.Request) {
	if h.sevenDayCheckIn == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.sevenDayCheckIn.GetSevenDayCheckInStatus(request.Context(), &activityv1.GetSevenDayCheckInStatusRequest{
		Meta:   httpkit.ActivityMeta(request),
		UserId: auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	groups, ok := h.resolveCheckinResourceGroups(writer, request, checkinRewardGroupIDs(resp.GetRewards()))
	if !ok {
		return
	}
	httpkit.WriteOK(writer, request, checkinStatusFromProto(resp, groups))
}

// signSevenDayCheckIn 只透传当前用户和客户端幂等命令，连续天数和发奖由 activity-service 决定。
func (h *Handler) signSevenDayCheckIn(writer http.ResponseWriter, request *http.Request) {
	if h.sevenDayCheckIn == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	var body checkinSignRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := body.normalizedCommandID()
	if commandID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.sevenDayCheckIn.SignSevenDayCheckIn(request.Context(), &activityv1.SignSevenDayCheckInRequest{
		Meta:      httpkit.ActivityMeta(request),
		UserId:    auth.UserIDFromContext(request.Context()),
		CommandId: commandID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, checkinSignData{
		ClaimID:         resp.GetClaimId(),
		CycleNo:         resp.GetCycleNo(),
		CheckinDay:      resp.GetCheckinDay(),
		DayIndex:        resp.GetDayIndex(),
		ResourceGroupID: resp.GetResourceGroupId(),
		Status:          resp.GetStatus(),
		WalletGrantID:   resp.GetWalletGrantId(),
		GrantedAtMS:     resp.GetGrantedAtMs(),
		ServerTimeMS:    resp.GetServerTimeMs(),
	})
}

func (h *Handler) resolveCheckinResourceGroups(writer http.ResponseWriter, request *http.Request, ids []int64) (map[int64]checkinResourceGroupData, bool) {
	ids = uniquePositiveIDs(ids)
	groups := make(map[int64]checkinResourceGroupData, len(ids))
	if len(ids) == 0 {
		return groups, true
	}
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return nil, false
	}
	for _, id := range ids {
		resp, err := h.walletClient.GetResourceGroup(request.Context(), &walletv1.GetResourceGroupRequest{
			RequestId: httpkit.RequestIDFromContext(request.Context()),
			AppCode:   appcode.FromContext(request.Context()),
			GroupId:   id,
		})
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return nil, false
		}
		group := checkinResourceGroupFromProto(resp.GetGroup())
		if group.GroupID > 0 {
			groups[group.GroupID] = group
		}
	}
	return groups, true
}

func checkinStatusFromProto(resp *activityv1.GetSevenDayCheckInStatusResponse, groups map[int64]checkinResourceGroupData) checkinStatusData {
	items := make([]checkinRewardData, 0, len(resp.GetRewards()))
	for _, reward := range resp.GetRewards() {
		item := checkinRewardData{
			DayIndex:        reward.GetDayIndex(),
			ResourceGroupID: reward.GetResourceGroupId(),
			Status:          reward.GetStatus(),
			ClaimID:         reward.GetClaimId(),
			WalletGrantID:   reward.GetWalletGrantId(),
			GrantedAtMS:     reward.GetGrantedAtMs(),
		}
		if group, ok := groups[reward.GetResourceGroupId()]; ok {
			item.ResourceGroup = &group
		}
		items = append(items, item)
	}
	return checkinStatusData{
		Enabled:         resp.GetEnabled(),
		CanSign:         resp.GetCanSign(),
		Reason:          resp.GetReason(),
		CycleNo:         resp.GetCycleNo(),
		CurrentDayIndex: resp.GetCurrentDayIndex(),
		NextDayIndex:    resp.GetNextDayIndex(),
		CheckinDay:      resp.GetCheckinDay(),
		LastCheckinDay:  resp.GetLastCheckinDay(),
		ConfigVersion:   resp.GetConfigVersion(),
		Rewards:         items,
		ServerTimeMS:    resp.GetServerTimeMs(),
		NextRefreshAtMS: resp.GetNextRefreshAtMs(),
	}
}

func checkinRewardGroupIDs(items []*activityv1.SevenDayCheckInRewardStatus) []int64 {
	ids := make([]int64, 0, len(items))
	for _, item := range items {
		if item.GetResourceGroupId() > 0 {
			ids = append(ids, item.GetResourceGroupId())
		}
	}
	return ids
}

func checkinResourceGroupFromProto(group *walletv1.ResourceGroup) checkinResourceGroupData {
	if group == nil {
		return checkinResourceGroupData{}
	}
	items := make([]checkinResourceGroupItemData, 0, len(group.GetItems()))
	for _, item := range group.GetItems() {
		items = append(items, checkinResourceGroupItemData{
			GroupItemID:       item.GetGroupItemId(),
			ItemType:          item.GetItemType(),
			ResourceID:        item.GetResourceId(),
			Resource:          checkinResourcePointerFromProto(item.GetResource()),
			WalletAssetType:   item.GetWalletAssetType(),
			WalletAssetAmount: item.GetWalletAssetAmount(),
			Quantity:          item.GetQuantity(),
			DurationMS:        item.GetDurationMs(),
			SortOrder:         item.GetSortOrder(),
		})
	}
	return checkinResourceGroupData{
		GroupID:     group.GetGroupId(),
		GroupCode:   group.GetGroupCode(),
		Name:        group.GetName(),
		Status:      group.GetStatus(),
		Description: group.GetDescription(),
		SortOrder:   group.GetSortOrder(),
		Items:       items,
		CreatedAtMS: group.GetCreatedAtMs(),
		UpdatedAtMS: group.GetUpdatedAtMs(),
	}
}

func checkinResourcePointerFromProto(item *walletv1.Resource) *checkinResourceData {
	if item == nil || item.GetResourceId() == 0 {
		return nil
	}
	resource := checkinResourceFromProto(item)
	return &resource
}

func checkinResourceFromProto(item *walletv1.Resource) checkinResourceData {
	if item == nil {
		return checkinResourceData{}
	}
	return checkinResourceData{
		ResourceID:        item.GetResourceId(),
		ResourceCode:      item.GetResourceCode(),
		ResourceType:      item.GetResourceType(),
		Name:              item.GetName(),
		Status:            item.GetStatus(),
		Grantable:         item.GetGrantable(),
		GrantStrategy:     item.GetGrantStrategy(),
		WalletAssetType:   item.GetWalletAssetType(),
		WalletAssetAmount: item.GetWalletAssetAmount(),
		UsageScopes:       item.GetUsageScopes(),
		AssetURL:          item.GetAssetUrl(),
		PreviewURL:        item.GetPreviewUrl(),
		AnimationURL:      item.GetAnimationUrl(),
		MetadataJSON:      item.GetMetadataJson(),
		SortOrder:         item.GetSortOrder(),
		CreatedAtMS:       item.GetCreatedAtMs(),
		UpdatedAtMS:       item.GetUpdatedAtMs(),
	}
}
