package walletapi

import (
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
	"net/http"
	"strings"

	userv1 "hyapp.local/api/proto/user/v1"
	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/services/gateway-service/internal/auth"
)

type rechargeProductData struct {
	ProductID         int64   `json:"product_id"`
	ProductCode       string  `json:"product_code"`
	ProductName       string  `json:"product_name"`
	Description       string  `json:"description"`
	Platform          string  `json:"platform"`
	Channel           string  `json:"channel"`
	CurrencyCode      string  `json:"currency_code"`
	CoinAmount        int64   `json:"coin_amount"`
	AmountMinor       int64   `json:"amount_minor"`
	AmountMicro       int64   `json:"amount_micro"`
	PolicyVersion     string  `json:"policy_version"`
	RegionIDs         []int64 `json:"region_ids"`
	ResourceAssetType string  `json:"resource_asset_type"`
	Status            string  `json:"status"`
	Enabled           bool    `json:"enabled"`
	SortOrder         int32   `json:"sort_order"`
}

type diamondExchangeRuleData struct {
	ExchangeType  string `json:"exchange_type"`
	FromAssetType string `json:"from_asset_type"`
	ToAssetType   string `json:"to_asset_type"`
	FromAmount    int64  `json:"from_amount"`
	ToAmount      int64  `json:"to_amount"`
	Enabled       bool   `json:"enabled"`
}

type walletOverviewData struct {
	Balances     []assetBalanceData     `json:"balances"`
	FeatureFlags walletFeatureFlagsData `json:"feature_flags"`
}

type walletFeatureFlagsData struct {
	RechargeEnabled        bool `json:"recharge_enabled"`
	DiamondExchangeEnabled bool `json:"diamond_exchange_enabled"`
}

type walletTransactionData struct {
	EntryID                   int64  `json:"entry_id"`
	TransactionID             string `json:"transaction_id"`
	BizType                   string `json:"biz_type"`
	AssetType                 string `json:"asset_type"`
	AvailableDelta            int64  `json:"available_delta"`
	FrozenDelta               int64  `json:"frozen_delta"`
	AvailableAfter            int64  `json:"available_after"`
	FrozenAfter               int64  `json:"frozen_after"`
	CounterpartyUserID        int64  `json:"counterparty_user_id"`
	CounterpartyDisplayUserID string `json:"counterparty_display_user_id,omitempty"`
	CounterpartyUsername      string `json:"counterparty_username,omitempty"`
	CounterpartyAvatar        string `json:"counterparty_avatar,omitempty"`
	RoomID                    string `json:"room_id"`
	CreatedAtMS               int64  `json:"created_at_ms"`
}

type giftWallData struct {
	Items             []giftWallItemData `json:"items"`
	GiftKindCount     int64              `json:"gift_kind_count"`
	GiftTotalCount    int64              `json:"gift_total_count"`
	TotalValue        int64              `json:"total_value"`
	TotalCoinValue    int64              `json:"total_coin_value"`
	TotalDiamondValue int64              `json:"total_diamond_value"`
	TotalGiftPoint    int64              `json:"total_gift_point"`
	TotalHeatValue    int64              `json:"total_heat_value"`
}

type giftWallItemData struct {
	GiftID               string        `json:"gift_id"`
	GiftName             string        `json:"gift_name"`
	ResourceID           int64         `json:"resource_id"`
	Resource             *resourceData `json:"resource,omitempty"`
	ResourceSnapshotJSON string        `json:"resource_snapshot_json"`
	GiftTypeCode         string        `json:"gift_type_code"`
	PresentationJSON     string        `json:"presentation_json"`
	GiftCount            int64         `json:"gift_count"`
	TotalValue           int64         `json:"total_value"`
	TotalCoinValue       int64         `json:"total_coin_value"`
	TotalDiamondValue    int64         `json:"total_diamond_value"`
	TotalGiftPoint       int64         `json:"total_gift_point"`
	TotalHeatValue       int64         `json:"total_heat_value"`
	ChargeAssetType      string        `json:"charge_asset_type"`
	LastPriceVersion     string        `json:"last_price_version"`
	FirstReceivedAtMS    int64         `json:"first_received_at_ms"`
	LastReceivedAtMS     int64         `json:"last_received_at_ms"`
	SortOrder            int32         `json:"sort_order"`
}

type googlePaymentConfirmRequestBody struct {
	CommandID       string `json:"command_id"`
	CommandIDAlt    string `json:"commandId"`
	ProductID       int64  `json:"product_id"`
	ProductIDAlt    int64  `json:"productId"`
	ProductCode     string `json:"product_code"`
	ProductCodeAlt  string `json:"productCode"`
	PackageName     string `json:"package_name"`
	PackageNameAlt  string `json:"packageName"`
	PurchaseToken   string `json:"purchase_token"`
	PurchaseAlt     string `json:"purchaseToken"`
	OrderID         string `json:"order_id"`
	OrderIDAlt      string `json:"orderId"`
	PurchaseTimeMS  int64  `json:"purchase_time_ms"`
	PurchaseTimeAlt int64  `json:"purchaseTimeMs"`
}

type googlePaymentConfirmData struct {
	PaymentOrderID   string           `json:"payment_order_id"`
	TransactionID    string           `json:"transaction_id"`
	Status           string           `json:"status"`
	ProductID        int64            `json:"product_id"`
	ProductCode      string           `json:"product_code"`
	CoinAmount       int64            `json:"coin_amount"`
	Balance          assetBalanceData `json:"balance"`
	IdempotentReplay bool             `json:"idempotent_replay"`
	ConsumeState     string           `json:"consume_state"`
}

type vipPurchaseRequestBody struct {
	CommandID    string `json:"command_id"`
	CommandIDAlt string `json:"commandId"`
	Level        int32  `json:"level"`
}

type vipPackageData struct {
	Level                      int32               `json:"level"`
	Name                       string              `json:"name"`
	Status                     string              `json:"status"`
	PriceCoin                  int64               `json:"price_coin"`
	DurationMS                 int64               `json:"duration_ms"`
	RewardResourceGroupID      int64               `json:"reward_resource_group_id"`
	RewardItems                []vipRewardItemData `json:"reward_items"`
	CanPurchase                bool                `json:"can_purchase"`
	SortOrder                  int32               `json:"sort_order"`
	RechargeGateRequired       bool                `json:"recharge_gate_required"`
	RequiredRechargeCoinAmount int64               `json:"required_recharge_coin_amount"`
	UserRechargeCoinAmount     int64               `json:"user_recharge_coin_amount"`
	PurchaseLockedReason       string              `json:"purchase_locked_reason"`
}

type vipRewardItemData struct {
	ResourceID   int64  `json:"resource_id"`
	ResourceCode string `json:"resource_code"`
	ResourceType string `json:"resource_type"`
	Name         string `json:"name"`
	Quantity     int64  `json:"quantity"`
	ExpiresAtMS  int64  `json:"expires_at_ms"`
	CoverURL     string `json:"cover_url"`
	AssetURL     string `json:"asset_url"`
	PreviewURL   string `json:"preview_url"`
	AnimationURL string `json:"animation_url"`
}

// getWalletOverview 返回钱包二级页摘要；我的页首屏使用更轻的 WalletValueSummary。
func (h *Handler) getWalletOverview(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.walletClient.GetWalletOverview(request.Context(), &walletv1.GetWalletOverviewRequest{
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
		UserId:    auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, walletOverviewFromProto(resp))
}

// listRechargeProducts 返回用户当前区域可用的充值档位。
func (h *Handler) listRechargeProducts(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil || h.userProfileClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	profileResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	resp, err := h.walletClient.ListRechargeProducts(request.Context(), &walletv1.ListRechargeProductsRequest{
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
		UserId:    userID,
		RegionId:  profileResp.GetUser().GetRegionId(),
		Platform:  httpkit.FirstQueryOrHeader(request, "platform", "X-App-Platform", "X-Platform"),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	products := make([]rechargeProductData, 0, len(resp.GetProducts()))
	for _, product := range resp.GetProducts() {
		products = append(products, rechargeProductFromProto(product))
	}
	httpkit.WriteOK(writer, request, map[string]any{"channels": resp.GetChannels(), "products": products})
}

// confirmGooglePayment 接收 App 支付成功后的 Google purchase_token，转交 wallet-service 做服务端校验和入账。
func (h *Handler) confirmGooglePayment(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil || h.userProfileClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	var body googlePaymentConfirmRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := strings.TrimSpace(body.CommandID)
	if commandID == "" {
		commandID = strings.TrimSpace(body.CommandIDAlt)
	}
	productID := body.ProductID
	if productID <= 0 {
		productID = body.ProductIDAlt
	}
	productCode := strings.TrimSpace(body.ProductCode)
	if productCode == "" {
		productCode = strings.TrimSpace(body.ProductCodeAlt)
	}
	packageName := strings.TrimSpace(body.PackageName)
	if packageName == "" {
		packageName = strings.TrimSpace(body.PackageNameAlt)
	}
	purchaseToken := strings.TrimSpace(body.PurchaseToken)
	if purchaseToken == "" {
		purchaseToken = strings.TrimSpace(body.PurchaseAlt)
	}
	orderID := strings.TrimSpace(body.OrderID)
	if orderID == "" {
		orderID = strings.TrimSpace(body.OrderIDAlt)
	}
	purchaseTimeMS := body.PurchaseTimeMS
	if purchaseTimeMS <= 0 {
		purchaseTimeMS = body.PurchaseTimeAlt
	}
	if commandID == "" || productID <= 0 || packageName == "" || purchaseToken == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	userID := auth.UserIDFromContext(request.Context())
	profileResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	resp, err := h.walletClient.ConfirmGooglePayment(request.Context(), &walletv1.ConfirmGooglePaymentRequest{
		RequestId:      httpkit.RequestIDFromContext(request.Context()),
		AppCode:        appcode.FromContext(request.Context()),
		CommandId:      commandID,
		UserId:         userID,
		RegionId:       profileResp.GetUser().GetRegionId(),
		ProductId:      productID,
		ProductCode:    productCode,
		PackageName:    packageName,
		PurchaseToken:  purchaseToken,
		OrderId:        orderID,
		PurchaseTimeMs: purchaseTimeMS,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, googlePaymentConfirmFromProto(resp))
}

// getDiamondExchangeConfig 返回钻石兑换配置。
func (h *Handler) getDiamondExchangeConfig(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.walletClient.GetDiamondExchangeConfig(request.Context(), &walletv1.GetDiamondExchangeConfigRequest{
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
		UserId:    auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	rules := make([]diamondExchangeRuleData, 0, len(resp.GetRules()))
	for _, rule := range resp.GetRules() {
		rules = append(rules, diamondExchangeRuleFromProto(rule))
	}
	httpkit.WriteOK(writer, request, map[string]any{"rules": rules})
}

// getMyGiftWall 返回当前登录用户已经收到的礼物墙；gateway 不接受 user_id，避免越权查询他人收礼事实。
func (h *Handler) getMyGiftWall(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.walletClient.GetUserGiftWall(request.Context(), &walletv1.GetUserGiftWallRequest{
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
		UserId:    auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, giftWallFromProto(resp))
}

// listWalletTransactions 返回当前用户钱包流水分页。
func (h *Handler) listWalletTransactions(writer http.ResponseWriter, request *http.Request) {
	h.listWalletTransactionsByAsset(writer, request, strings.TrimSpace(request.URL.Query().Get("asset_type")), 20)
}

// listCoinTransactions 返回当前用户金币流水分页；默认 page_size=30 对齐 App 最近 30 条展示。
func (h *Handler) listCoinTransactions(writer http.ResponseWriter, request *http.Request) {
	h.listWalletTransactionsByAsset(writer, request, "COIN", 30)
}

func (h *Handler) listWalletTransactionsByAsset(writer http.ResponseWriter, request *http.Request, assetType string, defaultPageSize int32) {
	if h.walletClient == 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", defaultPageSize)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.walletClient.ListWalletTransactions(request.Context(), &walletv1.ListWalletTransactionsRequest{
		RequestId: httpkit.RequestIDFromContext(request.Context()),
		AppCode:   appcode.FromContext(request.Context()),
		UserId:    auth.UserIDFromContext(request.Context()),
		AssetType: assetType,
		Page:      page,
		PageSize:  pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	profiles, err := h.walletTransactionCounterpartyProfiles(request, resp.GetTransactions())
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	items := make([]walletTransactionData, 0, len(resp.GetTransactions()))
	for _, item := range resp.GetTransactions() {
		items = append(items, walletTransactionFromProto(item, profiles))
	}
	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": resp.GetTotal(), "page": page, "page_size": pageSize})
}

func (h *Handler) walletTransactionCounterpartyProfiles(request *http.Request, transactions []*walletv1.WalletTransaction) (map[int64]*userv1.User, error) {
	if h.userProfileClient == nil {
		return map[int64]*userv1.User{}, nil
	}
	userIDs := make([]int64, 0, len(transactions))
	seen := make(map[int64]struct{}, len(transactions))
	for _, transaction := range transactions {
		userID := transaction.GetCounterpartyUserId()
		if userID <= 0 {
			continue
		}
		if _, exists := seen[userID]; exists {
			continue
		}
		seen[userID] = struct{}{}
		userIDs = append(userIDs, userID)
	}
	if len(userIDs) == 0 {
		return map[int64]*userv1.User{}, nil
	}
	// 钱包流水只保存稳定 user_id；展示昵称、短号和头像必须回 user-service 批量补齐，避免 H5 把长 user_id 当短号展示。
	resp, err := h.userProfileClient.BatchGetUsers(request.Context(), &userv1.BatchGetUsersRequest{
		Meta:    httpkit.UserMeta(request, ""),
		UserIds: userIDs,
	})
	if err != nil {
		return nil, err
	}
	return resp.GetUsers(), nil
}

func rechargeProductFromProto(product *walletv1.RechargeProduct) rechargeProductData {
	if product == nil {
		return rechargeProductData{}
	}
	return rechargeProductData{
		ProductID:         product.GetProductId(),
		ProductCode:       product.GetProductCode(),
		ProductName:       product.GetProductName(),
		Description:       product.GetDescription(),
		Platform:          product.GetPlatform(),
		Channel:           product.GetChannel(),
		CurrencyCode:      product.GetCurrencyCode(),
		CoinAmount:        product.GetCoinAmount(),
		AmountMinor:       product.GetAmountMinor(),
		AmountMicro:       product.GetAmountMicro(),
		PolicyVersion:     product.GetPolicyVersion(),
		RegionIDs:         product.GetRegionIds(),
		ResourceAssetType: product.GetResourceAssetType(),
		Status:            product.GetStatus(),
		Enabled:           product.GetEnabled(),
		SortOrder:         product.GetSortOrder(),
	}
}

func googlePaymentConfirmFromProto(resp *walletv1.ConfirmGooglePaymentResponse) googlePaymentConfirmData {
	if resp == nil {
		return googlePaymentConfirmData{}
	}
	return googlePaymentConfirmData{
		PaymentOrderID:   resp.GetPaymentOrderId(),
		TransactionID:    resp.GetTransactionId(),
		Status:           resp.GetStatus(),
		ProductID:        resp.GetProductId(),
		ProductCode:      resp.GetProductCode(),
		CoinAmount:       resp.GetCoinAmount(),
		Balance:          balanceFromProto(resp.GetBalance()),
		IdempotentReplay: resp.GetIdempotentReplay(),
		ConsumeState:     resp.GetConsumeState(),
	}
}

func diamondExchangeRuleFromProto(rule *walletv1.DiamondExchangeRule) diamondExchangeRuleData {
	if rule == nil {
		return diamondExchangeRuleData{}
	}
	return diamondExchangeRuleData{
		ExchangeType:  rule.GetExchangeType(),
		FromAssetType: rule.GetFromAssetType(),
		ToAssetType:   rule.GetToAssetType(),
		FromAmount:    rule.GetFromAmount(),
		ToAmount:      rule.GetToAmount(),
		Enabled:       rule.GetEnabled(),
	}
}

func walletOverviewFromProto(resp *walletv1.GetWalletOverviewResponse) walletOverviewData {
	if resp == nil {
		return walletOverviewData{}
	}
	balances := make([]assetBalanceData, 0, len(resp.GetBalances()))
	for _, balance := range resp.GetBalances() {
		balances = append(balances, balanceFromProto(balance))
	}
	flags := resp.GetFeatureFlags()
	return walletOverviewData{
		Balances: balances,
		FeatureFlags: walletFeatureFlagsData{
			RechargeEnabled:        flags.GetRechargeEnabled(),
			DiamondExchangeEnabled: flags.GetDiamondExchangeEnabled(),
		},
	}
}

func walletTransactionFromProto(item *walletv1.WalletTransaction, profiles map[int64]*userv1.User) walletTransactionData {
	if item == nil {
		return walletTransactionData{}
	}
	data := walletTransactionData{
		EntryID:            item.GetEntryId(),
		TransactionID:      item.GetTransactionId(),
		BizType:            item.GetBizType(),
		AssetType:          item.GetAssetType(),
		AvailableDelta:     item.GetAvailableDelta(),
		FrozenDelta:        item.GetFrozenDelta(),
		AvailableAfter:     item.GetAvailableAfter(),
		FrozenAfter:        item.GetFrozenAfter(),
		CounterpartyUserID: item.GetCounterpartyUserId(),
		RoomID:             item.GetRoomId(),
		CreatedAtMS:        item.GetCreatedAtMs(),
	}
	if profile := profiles[item.GetCounterpartyUserId()]; profile != nil {
		data.CounterpartyDisplayUserID = profile.GetDisplayUserId()
		data.CounterpartyUsername = profile.GetUsername()
		data.CounterpartyAvatar = profile.GetAvatar()
	}
	return data
}

func giftWallFromProto(resp *walletv1.GetUserGiftWallResponse) giftWallData {
	if resp == nil {
		return giftWallData{}
	}
	items := make([]giftWallItemData, 0, len(resp.GetItems()))
	for _, item := range resp.GetItems() {
		items = append(items, giftWallItemFromProto(item))
	}
	return giftWallData{
		Items:             items,
		GiftKindCount:     resp.GetGiftKindCount(),
		GiftTotalCount:    resp.GetGiftTotalCount(),
		TotalValue:        resp.GetTotalValue(),
		TotalCoinValue:    resp.GetTotalCoinValue(),
		TotalDiamondValue: resp.GetTotalDiamondValue(),
		TotalGiftPoint:    resp.GetTotalGiftPoint(),
		TotalHeatValue:    resp.GetTotalHeatValue(),
	}
}

func giftWallItemFromProto(item *walletv1.GiftWallItem) giftWallItemData {
	if item == nil {
		return giftWallItemData{}
	}
	return giftWallItemData{
		GiftID:               item.GetGiftId(),
		GiftName:             item.GetGiftName(),
		ResourceID:           item.GetResourceId(),
		Resource:             resourcePointerFromProto(item.GetResource()),
		ResourceSnapshotJSON: item.GetResourceSnapshotJson(),
		GiftTypeCode:         item.GetGiftTypeCode(),
		PresentationJSON:     item.GetPresentationJson(),
		GiftCount:            item.GetGiftCount(),
		TotalValue:           item.GetTotalValue(),
		TotalCoinValue:       item.GetTotalCoinValue(),
		TotalDiamondValue:    item.GetTotalDiamondValue(),
		TotalGiftPoint:       item.GetTotalGiftPoint(),
		TotalHeatValue:       item.GetTotalHeatValue(),
		ChargeAssetType:      item.GetChargeAssetType(),
		LastPriceVersion:     item.GetLastPriceVersion(),
		FirstReceivedAtMS:    item.GetFirstReceivedAtMs(),
		LastReceivedAtMS:     item.GetLastReceivedAtMs(),
		SortOrder:            item.GetSortOrder(),
	}
}

func balanceFromProto(balance *walletv1.AssetBalance) assetBalanceData {
	if balance == nil {
		return assetBalanceData{}
	}
	return assetBalanceData{
		AssetType:       balance.GetAssetType(),
		AvailableAmount: balance.GetAvailableAmount(),
		FrozenAmount:    balance.GetFrozenAmount(),
		Version:         balance.GetVersion(),
	}
}
