package grpc

import (
	"context"

	walletv1 "hyapp.local/api/proto/wallet/v1"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
)

// ListThirdPartyPaymentChannels 返回三方支付渠道、国家和方式树，后台用它直接渲染二级展开列表。
func (s *Server) ListThirdPartyPaymentChannels(ctx context.Context, req *walletv1.ListThirdPartyPaymentChannelsRequest) (*walletv1.ListThirdPartyPaymentChannelsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	channels, err := s.svc.ListThirdPartyPaymentChannels(ctx, ledger.ListThirdPartyPaymentChannelsQuery{
		AppCode:                req.GetAppCode(),
		ProviderCode:           req.GetProviderCode(),
		Status:                 req.GetStatus(),
		IncludeDisabledMethods: req.GetIncludeDisabledMethods(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.ListThirdPartyPaymentChannelsResponse{Channels: make([]*walletv1.ThirdPartyPaymentChannel, 0, len(channels))}
	for _, channel := range channels {
		resp.Channels = append(resp.Channels, thirdPartyPaymentChannelToProto(channel))
	}
	return resp, nil
}

// SetThirdPartyPaymentMethodStatus 开关某个国家下的具体支付方式；渠道本身不被连带关闭。
func (s *Server) SetThirdPartyPaymentMethodStatus(ctx context.Context, req *walletv1.SetThirdPartyPaymentMethodStatusRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	method, err := s.svc.SetThirdPartyPaymentMethodStatus(ctx, ledger.ThirdPartyPaymentMethodStatusCommand{
		AppCode:        req.GetAppCode(),
		MethodID:       req.GetMethodId(),
		Enabled:        req.GetEnabled(),
		OperatorUserID: req.GetOperatorUserId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.ThirdPartyPaymentMethodResponse{Method: thirdPartyPaymentMethodToProto(method)}, nil
}

// UpdateThirdPartyPaymentRate 保存 USD 到国家本币汇率；H5 只展示已配置正汇率的国家方式。
func (s *Server) UpdateThirdPartyPaymentRate(ctx context.Context, req *walletv1.UpdateThirdPartyPaymentRateRequest) (*walletv1.ThirdPartyPaymentMethodResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	method, err := s.svc.UpdateThirdPartyPaymentRate(ctx, ledger.ThirdPartyPaymentRateCommand{
		AppCode:           req.GetAppCode(),
		MethodID:          req.GetMethodId(),
		USDToCurrencyRate: req.GetUsdToCurrencyRate(),
		OperatorUserID:    req.GetOperatorUserId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.ThirdPartyPaymentMethodResponse{Method: thirdPartyPaymentMethodToProto(method)}, nil
}

// SyncThirdPartyPaymentMethods 从支付商同步当前商户实际开通的支付方式；写库仍由 wallet-service 统一负责。
func (s *Server) SyncThirdPartyPaymentMethods(ctx context.Context, req *walletv1.SyncThirdPartyPaymentMethodsRequest) (*walletv1.SyncThirdPartyPaymentMethodsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	result, err := s.svc.SyncThirdPartyPaymentMethods(ctx, ledger.ThirdPartyPaymentMethodSyncCommand{
		AppCode:        req.GetAppCode(),
		ProviderCode:   req.GetProviderCode(),
		OperatorUserID: req.GetOperatorUserId(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.SyncThirdPartyPaymentMethodsResponse{
		ProviderCode:        result.ProviderCode,
		ScannedCountryCount: int32(result.ScannedCountryCount),
		FetchedCountryCount: int32(result.FetchedCountryCount),
		CreatedCount:        int32(result.CreatedCount),
		UpdatedCount:        int32(result.UpdatedCount),
		SkippedCount:        int32(result.SkippedCount),
		Issues:              make([]*walletv1.ThirdPartyPaymentMethodSyncIssue, 0, len(result.Issues)),
	}
	for _, issue := range result.Issues {
		resp.Issues = append(resp.Issues, thirdPartyPaymentMethodSyncIssueToProto(issue))
	}
	return resp, nil
}

// ListH5RechargeOptions 返回 H5 当前账号的商品和支付方式，普通用户和币商由 audience_type 分流。
func (s *Server) ListH5RechargeOptions(ctx context.Context, req *walletv1.H5RechargeOptionsRequest) (*walletv1.H5RechargeOptionsResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	options, err := s.svc.ListH5RechargeOptions(ctx, ledger.H5RechargeOptionsQuery{
		AppCode:           req.GetAppCode(),
		TargetUserID:      req.GetTargetUserId(),
		TargetRegionID:    req.GetTargetRegionId(),
		TargetCountryCode: req.GetTargetCountryCode(),
		AudienceType:      req.GetAudienceType(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.H5RechargeOptionsResponse{
		Products:         make([]*walletv1.RechargeProduct, 0, len(options.Products)),
		PaymentMethods:   make([]*walletv1.ThirdPartyPaymentMethod, 0, len(options.PaymentMethods)),
		UsdtTrc20Enabled: options.USDTTRC20Enabled,
		UsdtTrc20Address: options.USDTTRC20Address,
	}
	for _, product := range options.Products {
		resp.Products = append(resp.Products, rechargeProductToProto(product))
	}
	for _, method := range options.PaymentMethods {
		resp.PaymentMethods = append(resp.PaymentMethods, thirdPartyPaymentMethodToProto(method))
	}
	return resp, nil
}

// CreateH5RechargeOrder 创建外部充值订单；MiFaPay 只返回 pay_url，USDT 只返回共享收款地址。
func (s *Server) CreateH5RechargeOrder(ctx context.Context, req *walletv1.CreateH5RechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, err := s.svc.CreateH5RechargeOrder(ctx, ledger.CreateExternalRechargeOrderCommand{
		AppCode:           req.GetAppCode(),
		CommandID:         req.GetCommandId(),
		TargetUserID:      req.GetTargetUserId(),
		TargetRegionID:    req.GetTargetRegionId(),
		TargetCountryID:   req.GetTargetCountryId(),
		TargetCountryCode: req.GetTargetCountryCode(),
		AudienceType:      req.GetAudienceType(),
		ProductID:         req.GetProductId(),
		ProviderCode:      req.GetProviderCode(),
		PaymentMethodID:   req.GetPaymentMethodId(),
		ReturnURL:         req.GetReturnUrl(),
		NotifyURL:         req.GetNotifyUrl(),
		ClientIP:          req.GetClientIp(),
		Language:          req.GetLanguage(),
		PayerName:         req.GetPayerName(),
		PayerAccount:      req.GetPayerAccount(),
		PayerEmail:        req.GetPayerEmail(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
}

// CreateTemporaryRechargeOrder 创建运营临时支付链接；支付成功只标记 paid，不触发钱包入账。
func (s *Server) CreateTemporaryRechargeOrder(ctx context.Context, req *walletv1.CreateTemporaryRechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, err := s.svc.CreateTemporaryRechargeOrder(ctx, ledger.CreateTemporaryExternalRechargeOrderCommand{
		AppCode:         req.GetAppCode(),
		CommandID:       req.GetCommandId(),
		CoinAmount:      req.GetCoinAmount(),
		USDMinorAmount:  req.GetUsdMinorAmount(),
		ProviderCode:    req.GetProviderCode(),
		PaymentMethodID: req.GetPaymentMethodId(),
		ReturnURL:       req.GetReturnUrl(),
		NotifyURL:       req.GetNotifyUrl(),
		ClientIP:        req.GetClientIp(),
		Language:        req.GetLanguage(),
		PayerName:       req.GetPayerName(),
		PayerAccount:    req.GetPayerAccount(),
		PayerEmail:      req.GetPayerEmail(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
}

// GetTemporaryRechargeOrder 给运营页验证临时链接是否已支付，并顺带触发三方查单刷新状态。
func (s *Server) GetTemporaryRechargeOrder(ctx context.Context, req *walletv1.GetTemporaryRechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, err := s.svc.GetTemporaryRechargeOrder(ctx, req.GetAppCode(), req.GetOrderId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
}

// ListTemporaryRechargeOrders 返回后台临时支付链接列表；这里只读订单快照，不批量访问上游支付商。
func (s *Server) ListTemporaryRechargeOrders(ctx context.Context, req *walletv1.ListTemporaryRechargeOrdersRequest) (*walletv1.ListTemporaryRechargeOrdersResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	orders, total, err := s.svc.ListTemporaryRechargeOrders(ctx, ledger.ListTemporaryExternalRechargeOrdersQuery{
		AppCode:      req.GetAppCode(),
		Status:       req.GetStatus(),
		ProviderCode: req.GetProviderCode(),
		Keyword:      req.GetKeyword(),
		Page:         req.GetPage(),
		PageSize:     req.GetPageSize(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	resp := &walletv1.ListTemporaryRechargeOrdersResponse{Orders: make([]*walletv1.ExternalRechargeOrder, 0, len(orders)), Total: total}
	for _, order := range orders {
		resp.Orders = append(resp.Orders, externalRechargeOrderToProto(order))
	}
	return resp, nil
}

// SubmitH5RechargeTx 接收 USDT tx_hash；链上校验和幂等入账仍在 wallet-service 事务里完成。
func (s *Server) SubmitH5RechargeTx(ctx context.Context, req *walletv1.SubmitH5RechargeTxRequest) (*walletv1.H5RechargeOrderResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, err := s.svc.SubmitH5RechargeTx(ctx, ledger.SubmitExternalRechargeTxCommand{
		AppCode:      req.GetAppCode(),
		OrderID:      req.GetOrderId(),
		TargetUserID: req.GetTargetUserId(),
		TxHash:       req.GetTxHash(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
}

// GetH5RechargeOrder 给 H5 轮询订单状态；target_user_id 用于避免无 token 场景越权看单。
func (s *Server) GetH5RechargeOrder(ctx context.Context, req *walletv1.GetH5RechargeOrderRequest) (*walletv1.H5RechargeOrderResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, err := s.svc.GetH5RechargeOrder(ctx, req.GetAppCode(), req.GetOrderId(), req.GetTargetUserId())
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return &walletv1.H5RechargeOrderResponse{Order: externalRechargeOrderToProto(order)}, nil
}

// VerifyCoinSellerRechargeReceipt 只读校验币商充值凭证，不创建外部订单，也不触发钱包入账。
func (s *Server) VerifyCoinSellerRechargeReceipt(ctx context.Context, req *walletv1.VerifyCoinSellerRechargeReceiptRequest) (*walletv1.VerifyCoinSellerRechargeReceiptResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	receipt, err := s.svc.VerifyCoinSellerRechargeReceipt(ctx, ledger.VerifyCoinSellerRechargeReceiptCommand{
		RequestID:            req.GetRequestId(),
		AppCode:              req.GetAppCode(),
		ProviderCode:         req.GetProviderCode(),
		ExternalOrderNo:      req.GetExternalOrderNo(),
		Chain:                req.GetChain(),
		USDMinorAmount:       req.GetUsdMinorAmount(),
		OrderDateMS:          req.GetOrderDateMs(),
		ProviderCountryCode:  req.GetProviderCountryCode(),
		ProviderCurrencyCode: req.GetProviderCurrencyCode(),
		ProviderAmountMinor:  req.GetProviderAmountMinor(),
		ProviderPayType:      req.GetProviderPayType(),
		ReceiveAddress:       req.GetReceiveAddress(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	return coinSellerRechargeReceiptVerificationToProto(receipt), nil
}

// HandleMifapayNotify 验签并处理 MiFaPay 回调；成功或重复成功由 HTTP 层按 response_text 返回大写 SUCCESS。
func (s *Server) HandleMifapayNotify(ctx context.Context, req *walletv1.HandleMifapayNotifyRequest) (*walletv1.HandleMifapayNotifyResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, accepted, err := s.svc.HandleMifapayNotify(ctx, req.GetAppCode(), ledger.MifaPayNotification{
		MerAccount: req.GetMerAccount(),
		Data:       req.GetData(),
		Sign:       req.GetSign(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	responseText := "FAIL"
	if accepted {
		responseText = "SUCCESS"
	}
	return &walletv1.HandleMifapayNotifyResponse{Accepted: accepted, ResponseText: responseText, OrderId: order.OrderID}, nil
}

// HandleV5PayNotify 验签并处理 V5Pay 回调；成功或重复成功由 HTTP 层按 response_text 返回小写 success。
func (s *Server) HandleV5PayNotify(ctx context.Context, req *walletv1.HandleV5PayNotifyRequest) (*walletv1.HandleV5PayNotifyResponse, error) {
	ctx = appcode.WithContext(ctx, req.GetAppCode())
	order, accepted, err := s.svc.HandleV5PayNotify(ctx, req.GetAppCode(), ledger.V5PayNotification{
		Fields:  req.GetFields(),
		RawJSON: req.GetRawJson(),
	})
	if err != nil {
		return nil, xerr.ToGRPCError(err)
	}
	responseText := "fail"
	if accepted {
		responseText = "success"
	}
	return &walletv1.HandleV5PayNotifyResponse{Accepted: accepted, ResponseText: responseText, OrderId: order.OrderID}, nil
}

func coinSellerRechargeReceiptVerificationToProto(receipt ledger.CoinSellerRechargeReceiptVerification) *walletv1.VerifyCoinSellerRechargeReceiptResponse {
	return &walletv1.VerifyCoinSellerRechargeReceiptResponse{
		Verified:            receipt.Verified,
		ProviderCode:        receipt.ProviderCode,
		ExternalOrderNo:     receipt.ExternalOrderNo,
		ProviderOrderId:     receipt.ProviderOrderID,
		Status:              receipt.Status,
		CurrencyCode:        receipt.CurrencyCode,
		ProviderAmountMinor: receipt.ProviderAmountMinor,
		RawJson:             receipt.RawJSON,
		Chain:               receipt.Chain,
		ReceiveAddress:      receipt.ReceiveAddress,
		VerifiedAtMs:        receipt.VerifiedAtMS,
		FailureReason:       receipt.FailureReason,
	}
}

func thirdPartyPaymentChannelToProto(channel ledger.ThirdPartyPaymentChannel) *walletv1.ThirdPartyPaymentChannel {
	resp := &walletv1.ThirdPartyPaymentChannel{
		AppCode:      channel.AppCode,
		ProviderCode: channel.ProviderCode,
		ProviderName: channel.ProviderName,
		Status:       channel.Status,
		SortOrder:    channel.SortOrder,
		Methods:      make([]*walletv1.ThirdPartyPaymentMethod, 0, len(channel.Methods)),
	}
	for _, method := range channel.Methods {
		resp.Methods = append(resp.Methods, thirdPartyPaymentMethodToProto(method))
	}
	return resp
}

func thirdPartyPaymentMethodSyncIssueToProto(issue ledger.ThirdPartyPaymentMethodSyncIssue) *walletv1.ThirdPartyPaymentMethodSyncIssue {
	return &walletv1.ThirdPartyPaymentMethodSyncIssue{
		CountryCode:  issue.CountryCode,
		CurrencyCode: issue.CurrencyCode,
		Code:         issue.Code,
		Message:      issue.Message,
	}
}

func thirdPartyPaymentMethodToProto(method ledger.ThirdPartyPaymentMethod) *walletv1.ThirdPartyPaymentMethod {
	return &walletv1.ThirdPartyPaymentMethod{
		MethodId:          method.MethodID,
		AppCode:           method.AppCode,
		ProviderCode:      method.ProviderCode,
		ProviderName:      method.ProviderName,
		CountryCode:       method.CountryCode,
		CountryName:       method.CountryName,
		CurrencyCode:      method.CurrencyCode,
		PayWay:            method.PayWay,
		PayType:           method.PayType,
		MethodName:        method.MethodName,
		LogoUrl:           method.LogoURL,
		Status:            method.Status,
		UsdToCurrencyRate: method.USDToCurrencyRate,
		SortOrder:         method.SortOrder,
		CreatedAtMs:       method.CreatedAtMS,
		UpdatedAtMs:       method.UpdatedAtMS,
	}
}

func externalRechargeOrderToProto(order ledger.ExternalRechargeOrder) *walletv1.ExternalRechargeOrder {
	return &walletv1.ExternalRechargeOrder{
		OrderId:             order.OrderID,
		AppCode:             order.AppCode,
		TargetUserId:        order.TargetUserID,
		TargetRegionId:      order.TargetRegionID,
		TargetCountryId:     order.TargetCountryID,
		TargetCountryCode:   order.TargetCountryCode,
		AudienceType:        order.AudienceType,
		ProductId:           order.ProductID,
		ProductName:         order.ProductName,
		CoinAmount:          order.CoinAmount,
		UsdMinorAmount:      order.USDMinorAmount,
		ProviderCode:        order.ProviderCode,
		PaymentMethodId:     order.PaymentMethodID,
		CountryCode:         order.CountryCode,
		CurrencyCode:        order.CurrencyCode,
		ProviderAmountMinor: order.ProviderAmountMinor,
		PayWay:              order.PayWay,
		PayType:             order.PayType,
		PayUrl:              order.PayURL,
		ProviderOrderId:     order.ProviderOrderID,
		TxHash:              order.TxHash,
		ReceiveAddress:      order.ReceiveAddress,
		Status:              order.Status,
		FailureReason:       order.FailureReason,
		TransactionId:       order.TransactionID,
		CreatedAtMs:         order.CreatedAtMS,
		UpdatedAtMs:         order.UpdatedAtMS,
		IdempotentReplay:    order.IdempotentReplay,
	}
}
