package walletapi

import (
	"net/http"
	"strconv"
	"strings"

	userv1 "hyapp.local/api/proto/user/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 coinSellerOverviewData struct {
	Profile     coinSellerProfileData    `json:"profile"`
	Permissions coinSellerPermissionData `json:"permissions"`
	Balance     assetBalanceData         `json:"balance"`
}

type coinSellerProfileData struct {
	UserID                  string `json:"user_id"`
	Status                  string `json:"status"`
	MerchantAssetType       string `json:"merchant_asset_type"`
	CanManageSubCoinSellers bool   `json:"can_manage_sub_coin_sellers"`
	CreatedByUserID         string `json:"created_by_user_id"`
	CreatedAtMS             int64  `json:"created_at_ms"`
	UpdatedAtMS             int64  `json:"updated_at_ms"`
}

type coinSellerPermissionData struct {
	CanManageSubCoinSellers bool `json:"can_manage_sub_coin_sellers"`
}

type createSubCoinSellerRequestBody struct {
	CommandID                string `json:"command_id"`
	CommandIDCamel           string `json:"commandId"`
	TargetDisplayUserID      string `json:"target_display_user_id"`
	TargetDisplayUserIDCamel string `json:"targetDisplayUserId"`
}

type coinSellerSubTransferRequestBody struct {
	CommandID      string `json:"command_id"`
	CommandIDCamel string `json:"commandId"`
	Amount         int64  `json:"amount"`
	Reason         string `json:"reason"`
}

type coinSellerSubTransferData struct {
	TransactionID      string `json:"transaction_id"`
	ParentBalanceAfter int64  `json:"parent_balance_after"`
	ChildBalanceAfter  int64  `json:"child_balance_after"`
	Amount             int64  `json:"amount"`
	CreatedAtMS        int64  `json:"created_at_ms"`
}

// getCoinSellerOverview 返回当前币商身份、子币商权限和专用库存余额。
func (h *Handler) getCoinSellerOverview(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil || h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	profile, ok := h.activeCoinSellerProfile(writer, request)
	if !ok {
		return
	}
	balance := assetBalanceData{AssetType: "COIN_SELLER_COIN"}
	resp, err := h.walletClient.GetBalances(request.Context(), &walletv1.GetBalancesRequest{
		RequestId:  httpkit.RequestIDFromContext(request.Context()),
		UserId:     auth.UserIDFromContext(request.Context()),
		AssetTypes: []string{"COIN_SELLER_COIN"},
		AppCode:    appcode.FromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	for _, item := range resp.GetBalances() {
		if item.GetAssetType() == "COIN_SELLER_COIN" {
			balance = assetBalanceData{
				AssetType:       item.GetAssetType(),
				AvailableAmount: item.GetAvailableAmount(),
				FrozenAmount:    item.GetFrozenAmount(),
				Version:         item.GetVersion(),
			}
			break
		}
	}
	httpkit.WriteOK(writer, request, coinSellerOverviewData{
		Profile:     coinSellerProfileFromProto(profile),
		Permissions: coinSellerPermissionData{CanManageSubCoinSellers: profile.GetCanManageSubCoinSellers()},
		Balance:     balance,
	})
}

// createSubCoinSeller 通过短 ID 提交子币商邀请申请；目标解析在 gateway，审核事实在 user-service 事务内落库。
func (h *Handler) createSubCoinSeller(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil || h.userIdentityClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	var body createSubCoinSellerRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := firstTrimmed(body.CommandID, body.CommandIDCamel)
	targetDisplayID := firstTrimmed(body.TargetDisplayUserID, body.TargetDisplayUserIDCamel)
	if commandID == "" || targetDisplayID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	targetUserID, ok := h.resolveCoinSellerTransferTargetUserID(writer, request, targetDisplayID)
	if !ok {
		return
	}
	resp, err := h.userHostClient.SubmitSubCoinSellerApplication(request.Context(), &userv1.SubmitSubCoinSellerApplicationRequest{
		Meta:         httpkit.UserMeta(request, ""),
		CommandId:    commandID,
		ParentUserId: auth.UserIDFromContext(request.Context()),
		TargetUserId: targetUserID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"application": coinSellerSubApplicationFromProto(resp.GetApplication()),
		"message":     "official will review your invitation within 24 hours",
	})
}

// listSubCoinSellers 返回当前父币商直属子币商列表；user-service 会在读取前校验权限开关。
func (h *Handler) listSubCoinSellers(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	page := positiveQueryInt(request, "page", 1)
	pageSize := positiveQueryInt(request, "page_size", 20)
	resp, err := h.userHostClient.ListSubCoinSellers(request.Context(), &userv1.ListSubCoinSellersRequest{
		Meta:         httpkit.UserMeta(request, ""),
		ParentUserId: auth.UserIDFromContext(request.Context()),
		Page:         int32(page),
		PageSize:     int32(pageSize),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	items := make([]coinSellerData, 0, len(resp.GetChildren()))
	for _, item := range resp.GetChildren() {
		items = append(items, coinSellerFromProto(item))
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"items":     items,
		"total":     resp.GetTotal(),
		"page":      resp.GetPage(),
		"page_size": resp.GetPageSize(),
	})
}

// transferCoinSellerStockToChild 把当前父币商库存转入直属子币商库存；校验关系后才调用 wallet-service。
func (h *Handler) transferCoinSellerStockToChild(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil || h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	childUserID, ok := httpkit.PositiveInt64PathValue(request, "child_user_id")
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	var body coinSellerSubTransferRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := firstTrimmed(body.CommandID, body.CommandIDCamel)
	reason := strings.TrimSpace(body.Reason)
	if commandID == "" || body.Amount <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	if reason == "" {
		reason = "coin seller sub transfer"
	}
	parentUserID := auth.UserIDFromContext(request.Context())
	checkResp, err := h.userHostClient.CheckCoinSellerSubRelation(request.Context(), &userv1.CheckCoinSellerSubRelationRequest{
		Meta:         httpkit.UserMeta(request, ""),
		ParentUserId: parentUserID,
		ChildUserId:  childUserID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	child := checkResp.GetChild()
	if !checkResp.GetAllowed() || child == nil || child.GetRegionId() <= 0 {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return
	}
	resp, err := h.walletClient.TransferCoinSellerStockToChild(request.Context(), &walletv1.TransferCoinSellerStockToChildRequest{
		CommandId:    commandID,
		ParentUserId: parentUserID,
		ChildUserId:  childUserID,
		Amount:       body.Amount,
		Reason:       reason,
		AppCode:      appcode.FromContext(request.Context()),
		RegionId:     child.GetRegionId(),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, coinSellerSubTransferData{
		TransactionID:      resp.GetTransactionId(),
		ParentBalanceAfter: resp.GetParentBalanceAfter(),
		ChildBalanceAfter:  resp.GetChildBalanceAfter(),
		Amount:             resp.GetAmount(),
		CreatedAtMS:        resp.GetCreatedAtMs(),
	})
}

func (h *Handler) activeCoinSellerProfile(writer http.ResponseWriter, request *http.Request) (*userv1.CoinSellerProfile, bool) {
	resp, err := h.userHostClient.GetCoinSellerProfile(request.Context(), &userv1.GetCoinSellerProfileRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return nil, false
	}
	profile := resp.GetCoinSellerProfile()
	if profile == nil || profile.GetStatus() != "active" || profile.GetMerchantAssetType() != "COIN_SELLER_COIN" {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return nil, false
	}
	return profile, true
}

func coinSellerProfileFromProto(profile *userv1.CoinSellerProfile) coinSellerProfileData {
	if profile == nil {
		return coinSellerProfileData{}
	}
	return coinSellerProfileData{
		UserID:                  int64IDString(profile.GetUserId()),
		Status:                  profile.GetStatus(),
		MerchantAssetType:       profile.GetMerchantAssetType(),
		CanManageSubCoinSellers: profile.GetCanManageSubCoinSellers(),
		CreatedByUserID:         int64IDString(profile.GetCreatedByUserId()),
		CreatedAtMS:             profile.GetCreatedAtMs(),
		UpdatedAtMS:             profile.GetUpdatedAtMs(),
	}
}

func coinSellerSubRelationFromProto(relation *userv1.CoinSellerSubRelation) map[string]any {
	if relation == nil {
		return map[string]any{}
	}
	return map[string]any{
		"relation_id":        int64IDString(relation.GetRelationId()),
		"parent_user_id":     int64IDString(relation.GetParentUserId()),
		"child_user_id":      int64IDString(relation.GetChildUserId()),
		"status":             relation.GetStatus(),
		"created_by_user_id": int64IDString(relation.GetCreatedByUserId()),
		"created_at_ms":      relation.GetCreatedAtMs(),
		"updated_at_ms":      relation.GetUpdatedAtMs(),
	}
}

func coinSellerSubApplicationFromProto(application *userv1.CoinSellerSubApplication) map[string]any {
	if application == nil {
		return map[string]any{}
	}
	return map[string]any{
		"application_id":       strconv.FormatInt(application.GetApplicationId(), 10),
		"applicationId":        strconv.FormatInt(application.GetApplicationId(), 10),
		"command_id":           application.GetCommandId(),
		"commandId":            application.GetCommandId(),
		"parent_user_id":       strconv.FormatInt(application.GetParentUserId(), 10),
		"parentUserId":         strconv.FormatInt(application.GetParentUserId(), 10),
		"target_user_id":       strconv.FormatInt(application.GetTargetUserId(), 10),
		"targetUserId":         strconv.FormatInt(application.GetTargetUserId(), 10),
		"status":               application.GetStatus(),
		"relation_id":          strconv.FormatInt(application.GetRelationId(), 10),
		"relationId":           strconv.FormatInt(application.GetRelationId(), 10),
		"reviewed_by_admin_id": strconv.FormatInt(application.GetReviewedByAdminId(), 10),
		"reviewedByAdminId":    strconv.FormatInt(application.GetReviewedByAdminId(), 10),
		"review_reason":        application.GetReviewReason(),
		"reviewReason":         application.GetReviewReason(),
		"reviewed_at_ms":       application.GetReviewedAtMs(),
		"reviewedAtMs":         application.GetReviewedAtMs(),
		"created_at_ms":        application.GetCreatedAtMs(),
		"createdAtMs":          application.GetCreatedAtMs(),
		"updated_at_ms":        application.GetUpdatedAtMs(),
		"updatedAtMs":          application.GetUpdatedAtMs(),
	}
}

func firstTrimmed(values ...string) string {
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return ""
}

func positiveQueryInt(request *http.Request, key string, fallback int) int {
	value, err := strconv.Atoi(strings.TrimSpace(request.URL.Query().Get(key)))
	if err != nil || value <= 0 {
		return fallback
	}
	return value
}
