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/pkg/xerr"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

const coinSellerSubSellerAssetType = "COIN_SELLER_COIN"

type flexibleJSONInt64 int64

func (v *flexibleJSONInt64) UnmarshalJSON(data []byte) error {
	raw := strings.TrimSpace(string(data))
	if raw == "" || raw == "null" {
		*v = 0
		return nil
	}
	if strings.HasPrefix(raw, `"`) {
		text, err := strconv.Unquote(raw)
		if err != nil {
			return err
		}
		raw = strings.TrimSpace(text)
		if raw == "" {
			*v = 0
			return nil
		}
	}
	parsed, err := strconv.ParseInt(raw, 10, 64)
	if err != nil {
		return err
	}
	*v = flexibleJSONInt64(parsed)
	return nil
}

func (v flexibleJSONInt64) Int64() int64 {
	return int64(v)
}

type coinSellerSubSellerPermissionData struct {
	Visible                   bool `json:"visible"`
	CanInviteSubSeller        bool `json:"can_invite_sub_seller"`
	CanTransferSubSeller      bool `json:"can_transfer_sub_seller"`
	CanInviteSubSellerCamel   bool `json:"canInviteSubSeller"`
	CanTransferSubSellerCamel bool `json:"canTransferSubSeller"`
}

type coinSellerSubSellerData struct {
	UserID                  string `json:"user_id"`
	DisplayUserID           string `json:"display_user_id"`
	Username                string `json:"username"`
	Avatar                  string `json:"avatar,omitempty"`
	CountryID               int64  `json:"country_id"`
	CountryCode             string `json:"country_code,omitempty"`
	CountryName             string `json:"country_name,omitempty"`
	CountryDisplayName      string `json:"country_display_name,omitempty"`
	RegionID                int64  `json:"region_id"`
	RegionCode              string `json:"region_code,omitempty"`
	RegionName              string `json:"region_name,omitempty"`
	Status                  string `json:"status"`
	MerchantAssetType       string `json:"merchant_asset_type"`
	ContactInfo             string `json:"contact_info"`
	WhatsappPhone           string `json:"whatsapp_phone"`
	WhatsappURL             string `json:"whatsapp_url"`
	CanManageSubCoinSellers bool   `json:"can_manage_sub_coin_sellers"`
	CoinSellerBalance       int64  `json:"coin_seller_balance"`
	AvailableAmount         int64  `json:"available_amount"`
	UpdatedAtMS             int64  `json:"updated_at_ms"`
}

type coinSellerSubSellerCreateRequestBody struct {
	CommandID                string            `json:"command_id"`
	CommandIDCamel           string            `json:"commandId"`
	ChildUserID              flexibleJSONInt64 `json:"child_user_id"`
	ChildUserIDCamel         flexibleJSONInt64 `json:"childUserId"`
	TargetUserID             flexibleJSONInt64 `json:"target_user_id"`
	TargetUserIDCamel        flexibleJSONInt64 `json:"targetUserId"`
	ChildDisplayUserID       string            `json:"child_display_user_id"`
	ChildDisplayUserIDCamel  string            `json:"childDisplayUserId"`
	TargetDisplayUserID      string            `json:"target_display_user_id"`
	TargetDisplayUserIDCamel string            `json:"targetDisplayUserId"`
}

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

func (b coinSellerSubSellerCreateRequestBody) normalizedChildUserID() int64 {
	for _, value := range []int64{b.ChildUserID.Int64(), b.ChildUserIDCamel.Int64(), b.TargetUserID.Int64(), b.TargetUserIDCamel.Int64()} {
		if value > 0 {
			return value
		}
	}
	return 0
}

func (b coinSellerSubSellerCreateRequestBody) normalizedChildDisplayUserID() string {
	for _, value := range []string{b.ChildDisplayUserID, b.ChildDisplayUserIDCamel, b.TargetDisplayUserID, b.TargetDisplayUserIDCamel} {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return ""
}

type coinSellerSubSellerTransferRequestBody struct {
	CommandID                string            `json:"command_id"`
	CommandIDCamel           string            `json:"commandId"`
	ChildUserID              flexibleJSONInt64 `json:"child_user_id"`
	ChildUserIDCamel         flexibleJSONInt64 `json:"childUserId"`
	TargetUserID             flexibleJSONInt64 `json:"target_user_id"`
	TargetUserIDCamel        flexibleJSONInt64 `json:"targetUserId"`
	ChildDisplayUserID       string            `json:"child_display_user_id"`
	ChildDisplayUserIDCamel  string            `json:"childDisplayUserId"`
	TargetDisplayUserID      string            `json:"target_display_user_id"`
	TargetDisplayUserIDCamel string            `json:"targetDisplayUserId"`
	Amount                   int64             `json:"amount"`
	Reason                   string            `json:"reason"`
}

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

func (b coinSellerSubSellerTransferRequestBody) normalizedChildUserID() int64 {
	for _, value := range []int64{b.ChildUserID.Int64(), b.ChildUserIDCamel.Int64(), b.TargetUserID.Int64(), b.TargetUserIDCamel.Int64()} {
		if value > 0 {
			return value
		}
	}
	return 0
}

func (b coinSellerSubSellerTransferRequestBody) normalizedChildDisplayUserID() string {
	for _, value := range []string{b.ChildDisplayUserID, b.ChildDisplayUserIDCamel, b.TargetDisplayUserID, b.TargetDisplayUserIDCamel} {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return ""
}

type coinSellerSubSellerTransferData 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"`
}

// getCoinSellerSubSellerPermission 返回 H5 币商中心子币商入口权限。
// 非币商或被关闭子币商权限时返回 false，而不是 4xx，让 H5 可以用同一个接口稳定决定入口显隐。
func (h *Handler) getCoinSellerSubSellerPermission(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	resp, err := h.userHostClient.GetCoinSellerProfile(request.Context(), &userv1.GetCoinSellerProfileRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: auth.UserIDFromContext(request.Context()),
	})
	if err != nil {
		if xerr.ReasonFromGRPC(err) == xerr.NotFound {
			httpkit.WriteOK(writer, request, coinSellerSubSellerPermission(false))
			return
		}
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	profile := resp.GetCoinSellerProfile()
	allowed := profile != nil &&
		profile.GetStatus() == "active" &&
		profile.GetMerchantAssetType() == coinSellerSubSellerAssetType &&
		profile.GetCanManageSubCoinSellers()
	httpkit.WriteOK(writer, request, coinSellerSubSellerPermission(allowed))
}

func coinSellerSubSellerPermission(allowed bool) coinSellerSubSellerPermissionData {
	return coinSellerSubSellerPermissionData{
		Visible:                   allowed,
		CanInviteSubSeller:        allowed,
		CanTransferSubSeller:      allowed,
		CanInviteSubSellerCamel:   allowed,
		CanTransferSubSellerCamel: allowed,
	}
}

// inviteCoinSellerSubSeller 通过短 ID 或内部 user_id 提交子币商邀请申请。
// user-service 在事务内校验父级权限、同区域、无环和子级唯一 active 父级，gateway 不复制这些规则。
func (h *Handler) inviteCoinSellerSubSeller(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 coinSellerSubSellerCreateRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := body.normalizedCommandID()
	childUserID, ok := h.resolveSubCoinSellerChildUserID(writer, request, body.normalizedChildUserID(), body.normalizedChildDisplayUserID())
	if !ok {
		return
	}
	if commandID == "" || childUserID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.userHostClient.SubmitSubCoinSellerApplication(request.Context(), &userv1.SubmitSubCoinSellerApplicationRequest{
		Meta:         httpkit.UserMeta(request, ""),
		CommandId:    commandID,
		ParentUserId: auth.UserIDFromContext(request.Context()),
		TargetUserId: childUserID,
	})
	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",
	})
}

// listCoinSellerSubSellers 返回直属子币商列表并补齐子币商专用库存余额。
func (h *Handler) listCoinSellerSubSellers(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
	}
	page, ok := positiveQueryInt32(request, "page", 1, 100000)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	pageSize, ok := positiveQueryInt32(request, "page_size", 20, 100)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.userHostClient.ListSubCoinSellers(request.Context(), &userv1.ListSubCoinSellersRequest{
		Meta:         httpkit.UserMeta(request, ""),
		ParentUserId: auth.UserIDFromContext(request.Context()),
		Page:         page,
		PageSize:     pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	items := make([]coinSellerSubSellerData, 0, len(resp.GetChildren()))
	for _, child := range resp.GetChildren() {
		balance, err := h.coinSellerSubBalance(request, child.GetUserId())
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return
		}
		items = append(items, coinSellerSubSellerFromProto(child, balance))
	}
	httpkit.WriteOK(writer, request, map[string]any{
		"items":     items,
		"children":  items,
		"total":     resp.GetTotal(),
		"page":      resp.GetPage(),
		"page_size": resp.GetPageSize(),
	})
}

// transferCoinSellerToSubSeller 先向 user-service 校验直属关系，再请求 wallet-service 做同资产库存划拨。
// 这个服务端校验是权限边界：前端隐藏按钮不能成为防绕过手段。
func (h *Handler) transferCoinSellerToSubSeller(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil || h.userIdentityClient == nil || h.walletClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	var body coinSellerSubSellerTransferRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := body.normalizedCommandID()
	childUserID, ok := h.resolveSubCoinSellerChildUserID(writer, request, body.normalizedChildUserID(), body.normalizedChildDisplayUserID())
	if !ok {
		return
	}
	if commandID == "" || childUserID <= 0 || body.Amount <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	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
	}
	if !checkResp.GetAllowed() || checkResp.GetChild() == nil || checkResp.GetChild().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:       strings.TrimSpace(body.Reason),
		AppCode:      appcode.FromContext(request.Context()),
		RegionId:     checkResp.GetChild().GetRegionId(),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, coinSellerSubSellerTransferData{
		TransactionID:      resp.GetTransactionId(),
		ParentBalanceAfter: resp.GetParentBalanceAfter(),
		ChildBalanceAfter:  resp.GetChildBalanceAfter(),
		Amount:             resp.GetAmount(),
		CreatedAtMS:        resp.GetCreatedAtMs(),
	})
}

func (h *Handler) resolveSubCoinSellerChildUserID(writer http.ResponseWriter, request *http.Request, childUserID int64, displayUserID string) (int64, bool) {
	if childUserID > 0 {
		return childUserID, true
	}
	displayUserID = strings.TrimSpace(displayUserID)
	if displayUserID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return 0, false
	}
	return h.resolveCoinSellerTransferTargetUserID(writer, request, displayUserID)
}

func (h *Handler) coinSellerSubBalance(request *http.Request, userID int64) (int64, error) {
	resp, err := h.walletClient.GetBalances(request.Context(), &walletv1.GetBalancesRequest{
		RequestId:  httpkit.RequestIDFromContext(request.Context()),
		UserId:     userID,
		AssetTypes: []string{coinSellerSubSellerAssetType},
		AppCode:    appcode.FromContext(request.Context()),
	})
	if err != nil {
		return 0, err
	}
	for _, balance := range resp.GetBalances() {
		if strings.EqualFold(balance.GetAssetType(), coinSellerSubSellerAssetType) {
			return balance.GetAvailableAmount(), nil
		}
	}
	return 0, nil
}

func coinSellerSubSellerFromProto(item *userv1.CoinSellerListItem, balance int64) coinSellerSubSellerData {
	if item == nil {
		return coinSellerSubSellerData{}
	}
	contactInfo := strings.TrimSpace(item.GetContactInfo())
	return coinSellerSubSellerData{
		UserID:                  int64IDString(item.GetUserId()),
		DisplayUserID:           item.GetDisplayUserId(),
		Username:                item.GetUsername(),
		Avatar:                  item.GetAvatar(),
		CountryID:               item.GetCountryId(),
		CountryCode:             item.GetCountryCode(),
		CountryName:             item.GetCountryName(),
		CountryDisplayName:      item.GetCountryDisplayName(),
		RegionID:                item.GetRegionId(),
		RegionCode:              item.GetRegionCode(),
		RegionName:              item.GetRegionName(),
		Status:                  item.GetStatus(),
		MerchantAssetType:       item.GetMerchantAssetType(),
		ContactInfo:             contactInfo,
		WhatsappPhone:           contactInfo,
		WhatsappURL:             coinSellerWhatsAppURL(contactInfo),
		CanManageSubCoinSellers: item.GetCanManageSubCoinSellers(),
		CoinSellerBalance:       balance,
		AvailableAmount:         balance,
		UpdatedAtMS:             item.GetUpdatedAtMs(),
	}
}

func positiveQueryInt32(request *http.Request, name string, fallback int32, max int32) (int32, bool) {
	raw := strings.TrimSpace(request.URL.Query().Get(name))
	if raw == "" {
		return fallback, true
	}
	value, err := strconv.ParseInt(raw, 10, 32)
	if err != nil || value <= 0 || value > int64(max) {
		return 0, false
	}
	return int32(value), true
}
