package walletapi

import (
	"encoding/json"
	"fmt"
	"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"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
	"hyapp/services/gateway-service/internal/transport/http/useridinput"
)

const (
	h5RechargeAudienceNormal     = "normal"
	h5RechargeAudienceCoinSeller = "coin_seller"
	h5PaymentProviderMifaPay     = "mifapay"
	h5PaymentProviderUSDTTRC20   = "usdt_trc20"
)

type h5RechargeTargetContext struct {
	UserID           int64
	DisplayUserID    string
	Username         string
	Avatar           string
	CountryCode      string
	CountryName      string
	RegionID         int64
	RegionCode       string
	RegionName       string
	AudienceType     string
	IsCoinSeller     bool
	CoinSellerStatus string
	ResolvedByToken  bool
}

type h5RechargeContextData struct {
	Account  h5RechargeAccountData `json:"account"`
	Role     string                `json:"role"`
	CanPay   bool                  `json:"can_pay"`
	AuthMode string                `json:"auth_mode"`
}

type h5RechargeAccountData struct {
	DisplayUserID    string `json:"display_user_id"`
	Username         string `json:"username"`
	Avatar           string `json:"avatar,omitempty"`
	CountryCode      string `json:"country_code,omitempty"`
	CountryName      string `json:"country_name,omitempty"`
	RegionID         int64  `json:"region_id"`
	RegionCode       string `json:"region_code,omitempty"`
	RegionName       string `json:"region_name,omitempty"`
	IsCoinSeller     bool   `json:"is_coin_seller"`
	CoinSellerStatus string `json:"coin_seller_status,omitempty"`
	AudienceType     string `json:"audience_type"`
}

type h5RechargeOptionsData struct {
	Account          h5RechargeAccountData         `json:"account"`
	Products         []rechargeProductData         `json:"products"`
	PaymentMethods   []thirdPartyPaymentMethodData `json:"payment_methods"`
	USDTTRC20Enabled bool                          `json:"usdt_trc20_enabled"`
	USDTTRC20Address string                        `json:"usdt_trc20_address,omitempty"`
}

type thirdPartyPaymentMethodData struct {
	MethodID          int64  `json:"method_id"`
	ProviderCode      string `json:"provider_code"`
	ProviderName      string `json:"provider_name"`
	CountryCode       string `json:"country_code"`
	CountryName       string `json:"country_name"`
	CurrencyCode      string `json:"currency_code"`
	PayWay            string `json:"pay_way"`
	PayType           string `json:"pay_type"`
	MethodName        string `json:"method_name"`
	LogoURL           string `json:"logo_url,omitempty"`
	Status            string `json:"status"`
	USDToCurrencyRate string `json:"usd_to_currency_rate"`
	SortOrder         int32  `json:"sort_order"`
}

type h5RechargeOrderRequestBody struct {
	DisplayUserID      string `json:"display_user_id"`
	ProductID          int64  `json:"product_id"`
	ProductIDAlt       int64  `json:"productId"`
	ProviderCode       string `json:"provider_code"`
	ProviderCodeAlt    string `json:"providerCode"`
	PaymentMethod      string `json:"payment_method"`
	PaymentMethodID    int64  `json:"payment_method_id"`
	PaymentMethodIDAlt int64  `json:"paymentMethodId"`
	CommandID          string `json:"command_id"`
	CommandIDAlt       string `json:"commandId"`
	ReturnURL          string `json:"return_url"`
	ReturnURLAlt       string `json:"returnUrl"`
}

type h5RechargeTxRequestBody struct {
	DisplayUserID string `json:"display_user_id"`
	TxHash        string `json:"tx_hash"`
	TxHashAlt     string `json:"txHash"`
}

type h5RechargeOrderData struct {
	OrderID             string `json:"order_id"`
	ProductID           int64  `json:"product_id"`
	ProductName         string `json:"product_name"`
	CoinAmount          int64  `json:"coin_amount"`
	USDMinorAmount      int64  `json:"usd_minor_amount"`
	AudienceType        string `json:"audience_type"`
	ProviderCode        string `json:"provider_code"`
	PaymentMethodID     int64  `json:"payment_method_id"`
	CountryCode         string `json:"country_code"`
	CurrencyCode        string `json:"currency_code"`
	ProviderAmountMinor int64  `json:"provider_amount_minor"`
	PayWay              string `json:"pay_way"`
	PayType             string `json:"pay_type"`
	PayURL              string `json:"pay_url,omitempty"`
	TxHash              string `json:"tx_hash,omitempty"`
	ReceiveAddress      string `json:"receive_address,omitempty"`
	Status              string `json:"status"`
	FailureReason       string `json:"failure_reason,omitempty"`
	TransactionID       string `json:"transaction_id,omitempty"`
	IdempotentReplay    bool   `json:"idempotent_replay"`
	CreatedAtMS         int64  `json:"created_at_ms"`
	UpdatedAtMS         int64  `json:"updated_at_ms"`
}

type mifapayNotifyBody struct {
	MerAccount string `json:"merAccount"`
	Data       string `json:"data"`
	Sign       string `json:"sign"`
}

// getH5RechargeContext 确认 H5 充值目标账号；带 token 时只认 token 用户，不接受前端指定别人。
func (h *Handler) getH5RechargeContext(writer http.ResponseWriter, request *http.Request) {
	target, ok := h.resolveH5RechargeTarget(writer, request, request.URL.Query().Get("display_user_id"))
	if !ok {
		return
	}
	httpkit.WriteOK(writer, request, h5RechargeContextData{
		Account:  h5RechargeAccountFromTarget(target),
		Role:     target.AudienceType,
		CanPay:   target.RegionID > 0,
		AuthMode: h5RechargeAuthMode(target.ResolvedByToken),
	})
}

// listH5RechargeOptions 返回已确认账号的商品档位、USDT 和可下单的 MiFaPay 方式。
func (h *Handler) listH5RechargeOptions(writer http.ResponseWriter, request *http.Request) {
	target, ok := h.resolveH5RechargeTarget(writer, request, request.URL.Query().Get("display_user_id"))
	if !ok {
		return
	}
	resp, err := h.walletClient.ListH5RechargeOptions(request.Context(), &walletv1.H5RechargeOptionsRequest{
		RequestId:         httpkit.RequestIDFromContext(request.Context()),
		AppCode:           appcode.FromContext(request.Context()),
		TargetUserId:      target.UserID,
		TargetRegionId:    target.RegionID,
		TargetCountryCode: target.CountryCode,
		AudienceType:      target.AudienceType,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, h5RechargeOptionsFromProto(target, resp))
}

// createH5RechargeOrder 创建 MiFaPay 或 USDT 外部充值订单；目标账号始终由 gateway 重新解析。
func (h *Handler) createH5RechargeOrder(writer http.ResponseWriter, request *http.Request) {
	var body h5RechargeOrderRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	target, ok := h.resolveH5RechargeTarget(writer, request, body.DisplayUserID)
	if !ok {
		return
	}
	productID := firstPositiveInt64(body.ProductID, body.ProductIDAlt)
	paymentMethodID := firstPositiveInt64(body.PaymentMethodID, body.PaymentMethodIDAlt)
	providerCode := normalizeH5PaymentProvider(firstNonEmptyString(body.ProviderCode, body.ProviderCodeAlt, body.PaymentMethod), paymentMethodID)
	commandID := strings.TrimSpace(firstNonEmptyString(body.CommandID, body.CommandIDAlt))
	if commandID == "" {
		commandID = fmt.Sprintf("h5_recharge:%d:%s", target.UserID, httpkit.RequestIDFromContext(request.Context()))
	}
	if productID <= 0 || providerCode == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.walletClient.CreateH5RechargeOrder(request.Context(), &walletv1.CreateH5RechargeOrderRequest{
		RequestId:         httpkit.RequestIDFromContext(request.Context()),
		AppCode:           appcode.FromContext(request.Context()),
		CommandId:         commandID,
		TargetUserId:      target.UserID,
		TargetRegionId:    target.RegionID,
		TargetCountryCode: target.CountryCode,
		AudienceType:      target.AudienceType,
		ProductId:         productID,
		ProviderCode:      providerCode,
		PaymentMethodId:   paymentMethodID,
		ReturnUrl:         firstNonEmptyString(body.ReturnURL, body.ReturnURLAlt),
		ClientIp:          httpkit.ClientIP(request),
		Language:          httpkit.FirstQueryOrHeader(request, "language", "X-Language", "Accept-Language"),
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"order": h5RechargeOrderFromProto(resp.GetOrder())})
}

// submitH5RechargeTx 提交 USDT tx_hash；无 token 时仍用 display_user_id 校验订单归属。
func (h *Handler) submitH5RechargeTx(writer http.ResponseWriter, request *http.Request) {
	var body h5RechargeTxRequestBody
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	target, ok := h.resolveH5RechargeTarget(writer, request, body.DisplayUserID)
	if !ok {
		return
	}
	orderID := strings.TrimSpace(request.PathValue("order_id"))
	txHash := strings.TrimSpace(firstNonEmptyString(body.TxHash, body.TxHashAlt))
	if orderID == "" || txHash == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.walletClient.SubmitH5RechargeTx(request.Context(), &walletv1.SubmitH5RechargeTxRequest{
		RequestId:    httpkit.RequestIDFromContext(request.Context()),
		AppCode:      appcode.FromContext(request.Context()),
		OrderId:      orderID,
		TargetUserId: target.UserID,
		TxHash:       txHash,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"order": h5RechargeOrderFromProto(resp.GetOrder())})
}

// getH5RechargeOrder 给 H5 轮询订单状态；gateway 每次重新解析账号，避免前端伪造 target_user_id。
func (h *Handler) getH5RechargeOrder(writer http.ResponseWriter, request *http.Request) {
	target, ok := h.resolveH5RechargeTarget(writer, request, request.URL.Query().Get("display_user_id"))
	if !ok {
		return
	}
	orderID := strings.TrimSpace(request.PathValue("order_id"))
	if orderID == "" {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.walletClient.GetH5RechargeOrder(request.Context(), &walletv1.GetH5RechargeOrderRequest{
		RequestId:    httpkit.RequestIDFromContext(request.Context()),
		AppCode:      appcode.FromContext(request.Context()),
		OrderId:      orderID,
		TargetUserId: target.UserID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"order": h5RechargeOrderFromProto(resp.GetOrder())})
}

// handleMifapayNotify 按 MiFaPay 要求返回纯文本 SUCCESS；失败不套 gateway JSON envelope。
func (h *Handler) handleMifapayNotify(writer http.ResponseWriter, request *http.Request) {
	if h.walletClient == nil {
		writeMifaPayNotifyText(writer, http.StatusBadGateway, "FAIL")
		return
	}
	body := mifapayNotifyBody{
		MerAccount: strings.TrimSpace(request.FormValue("merAccount")),
		Data:       strings.TrimSpace(request.FormValue("data")),
		Sign:       strings.TrimSpace(request.FormValue("sign")),
	}
	if body.MerAccount == "" && body.Data == "" && body.Sign == "" {
		if err := json.NewDecoder(request.Body).Decode(&body); err != nil {
			writeMifaPayNotifyText(writer, http.StatusBadRequest, "FAIL")
			return
		}
	}
	resp, err := h.walletClient.HandleMifapayNotify(request.Context(), &walletv1.HandleMifapayNotifyRequest{
		RequestId:  httpkit.RequestIDFromContext(request.Context()),
		AppCode:    appcode.FromContext(request.Context()),
		MerAccount: body.MerAccount,
		Data:       body.Data,
		Sign:       body.Sign,
	})
	if err != nil || !resp.GetAccepted() {
		writeMifaPayNotifyText(writer, http.StatusBadRequest, "FAIL")
		return
	}
	writeMifaPayNotifyText(writer, http.StatusOK, "SUCCESS")
}

func (h *Handler) resolveH5RechargeTarget(writer http.ResponseWriter, request *http.Request, displayUserID string) (h5RechargeTargetContext, bool) {
	if h.walletClient == nil || h.userProfileClient == nil || h.userIdentityClient == nil || h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return h5RechargeTargetContext{}, false
	}
	userID := auth.UserIDFromContext(request.Context())
	resolvedByToken := userID > 0
	if !resolvedByToken {
		// 无 token 入口的输入是“用户标识”，可能是 active 靓号、当前短号、默认数字 ID 或真实 user_id。
		// 所有传给 wallet-service 的目标都必须先在 gateway 统一收敛成内部 user_id，避免各业务入口自行解析导致靓号报错。
		resolvedUserID, err := useridinput.Resolve(request.Context(), h.userIdentityClient, httpkit.UserMeta(request, ""), displayUserID)
		if err != nil {
			httpkit.WriteRPCError(writer, request, err)
			return h5RechargeTargetContext{}, false
		}
		userID = resolvedUserID
	}
	if userID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return h5RechargeTargetContext{}, false
	}
	profileResp, err := h.userProfileClient.GetUser(request.Context(), &userv1.GetUserRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return h5RechargeTargetContext{}, false
	}
	user := profileResp.GetUser()
	if user == nil || user.GetUserId() <= 0 || user.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE || user.GetRegionId() <= 0 {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "permission denied")
		return h5RechargeTargetContext{}, false
	}
	audienceType, isCoinSeller, coinSellerStatus, ok := h.resolveH5RechargeAudience(writer, request, userID)
	if !ok {
		return h5RechargeTargetContext{}, false
	}
	return h5RechargeTargetContext{
		UserID:           user.GetUserId(),
		DisplayUserID:    user.GetDisplayUserId(),
		Username:         user.GetUsername(),
		Avatar:           user.GetAvatar(),
		CountryCode:      strings.ToUpper(strings.TrimSpace(user.GetCountry())),
		CountryName:      firstNonEmptyString(user.GetCountryDisplayName(), user.GetCountryName()),
		RegionID:         user.GetRegionId(),
		RegionCode:       user.GetRegionCode(),
		RegionName:       user.GetRegionName(),
		AudienceType:     audienceType,
		IsCoinSeller:     isCoinSeller,
		CoinSellerStatus: coinSellerStatus,
		ResolvedByToken:  resolvedByToken,
	}, true
}

func (h *Handler) resolveH5RechargeAudience(writer http.ResponseWriter, request *http.Request, userID int64) (string, bool, string, bool) {
	resp, err := h.userHostClient.GetUserRoleSummary(request.Context(), &userv1.GetUserRoleSummaryRequest{
		Meta:   httpkit.UserMeta(request, ""),
		UserId: userID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return "", false, "", false
	}
	summary := resp.GetSummary()
	if summary != nil && summary.GetIsCoinSeller() && strings.EqualFold(summary.GetCoinSellerStatus(), "active") {
		return h5RechargeAudienceCoinSeller, true, summary.GetCoinSellerStatus(), true
	}
	status := ""
	if summary != nil {
		status = summary.GetCoinSellerStatus()
	}
	return h5RechargeAudienceNormal, false, status, true
}

func h5RechargeOptionsFromProto(target h5RechargeTargetContext, resp *walletv1.H5RechargeOptionsResponse) h5RechargeOptionsData {
	data := h5RechargeOptionsData{Account: h5RechargeAccountFromTarget(target)}
	if resp == nil {
		return data
	}
	data.Products = make([]rechargeProductData, 0, len(resp.GetProducts()))
	for _, product := range resp.GetProducts() {
		data.Products = append(data.Products, rechargeProductFromProto(product))
	}
	data.PaymentMethods = make([]thirdPartyPaymentMethodData, 0, len(resp.GetPaymentMethods()))
	for _, method := range resp.GetPaymentMethods() {
		data.PaymentMethods = append(data.PaymentMethods, thirdPartyPaymentMethodFromProto(method))
	}
	data.USDTTRC20Enabled = resp.GetUsdtTrc20Enabled()
	data.USDTTRC20Address = resp.GetUsdtTrc20Address()
	return data
}

func h5RechargeAccountFromTarget(target h5RechargeTargetContext) h5RechargeAccountData {
	return h5RechargeAccountData{
		DisplayUserID:    target.DisplayUserID,
		Username:         target.Username,
		Avatar:           target.Avatar,
		CountryCode:      target.CountryCode,
		CountryName:      target.CountryName,
		RegionID:         target.RegionID,
		RegionCode:       target.RegionCode,
		RegionName:       target.RegionName,
		IsCoinSeller:     target.IsCoinSeller,
		CoinSellerStatus: target.CoinSellerStatus,
		AudienceType:     target.AudienceType,
	}
}

func thirdPartyPaymentMethodFromProto(method *walletv1.ThirdPartyPaymentMethod) thirdPartyPaymentMethodData {
	if method == nil {
		return thirdPartyPaymentMethodData{}
	}
	return thirdPartyPaymentMethodData{
		MethodID:          method.GetMethodId(),
		ProviderCode:      method.GetProviderCode(),
		ProviderName:      method.GetProviderName(),
		CountryCode:       method.GetCountryCode(),
		CountryName:       method.GetCountryName(),
		CurrencyCode:      method.GetCurrencyCode(),
		PayWay:            method.GetPayWay(),
		PayType:           method.GetPayType(),
		MethodName:        method.GetMethodName(),
		LogoURL:           method.GetLogoUrl(),
		Status:            method.GetStatus(),
		USDToCurrencyRate: method.GetUsdToCurrencyRate(),
		SortOrder:         method.GetSortOrder(),
	}
}

func h5RechargeOrderFromProto(order *walletv1.ExternalRechargeOrder) h5RechargeOrderData {
	if order == nil {
		return h5RechargeOrderData{}
	}
	return h5RechargeOrderData{
		OrderID:             order.GetOrderId(),
		ProductID:           order.GetProductId(),
		ProductName:         order.GetProductName(),
		CoinAmount:          order.GetCoinAmount(),
		USDMinorAmount:      order.GetUsdMinorAmount(),
		AudienceType:        order.GetAudienceType(),
		ProviderCode:        order.GetProviderCode(),
		PaymentMethodID:     order.GetPaymentMethodId(),
		CountryCode:         order.GetCountryCode(),
		CurrencyCode:        order.GetCurrencyCode(),
		ProviderAmountMinor: order.GetProviderAmountMinor(),
		PayWay:              order.GetPayWay(),
		PayType:             order.GetPayType(),
		PayURL:              order.GetPayUrl(),
		TxHash:              order.GetTxHash(),
		ReceiveAddress:      order.GetReceiveAddress(),
		Status:              order.GetStatus(),
		FailureReason:       order.GetFailureReason(),
		TransactionID:       order.GetTransactionId(),
		IdempotentReplay:    order.GetIdempotentReplay(),
		CreatedAtMS:         order.GetCreatedAtMs(),
		UpdatedAtMS:         order.GetUpdatedAtMs(),
	}
}

func normalizeH5PaymentProvider(value string, paymentMethodID int64) string {
	value = strings.ToLower(strings.TrimSpace(value))
	switch value {
	case "usdt", "usdt-trc20", "usdt_trc20", "trc20":
		return h5PaymentProviderUSDTTRC20
	case "mifa", "mifapay", "third_party":
		return h5PaymentProviderMifaPay
	case "":
		if paymentMethodID > 0 {
			return h5PaymentProviderMifaPay
		}
	}
	return value
}

func h5RechargeAuthMode(resolvedByToken bool) string {
	if resolvedByToken {
		return "token"
	}
	return "display_user_id"
}

func firstPositiveInt64(values ...int64) int64 {
	for _, value := range values {
		if value > 0 {
			return value
		}
	}
	return 0
}

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

func writeMifaPayNotifyText(writer http.ResponseWriter, statusCode int, body string) {
	writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
	writer.WriteHeader(statusCode)
	_, _ = writer.Write([]byte(body))
}
