package payment

import (
	"fmt"
	"strconv"
	"strings"

	walletv1 "hyapp.local/api/proto/wallet/v1"
)

type rechargeBillDTO struct {
	AppCode                string              `json:"appCode"`
	TransactionID          string              `json:"transactionId"`
	CommandID              string              `json:"commandId"`
	RechargeType           string              `json:"rechargeType"`
	Status                 string              `json:"status"`
	ExternalRef            string              `json:"externalRef"`
	UserID                 int64               `json:"userId,string"`
	SellerUserID           int64               `json:"sellerUserId,string"`
	SellerRegionID         int64               `json:"sellerRegionId"`
	TargetRegionID         int64               `json:"targetRegionId"`
	PolicyID               int64               `json:"policyId"`
	PolicyVersion          string              `json:"policyVersion"`
	CurrencyCode           string              `json:"currencyCode"`
	CoinAmount             int64               `json:"coinAmount"`
	USDMinorAmount         int64               `json:"usdMinorAmount"`
	ExchangeCoinAmount     int64               `json:"exchangeCoinAmount"`
	ExchangeUSDMinorAmount int64               `json:"exchangeUsdMinorAmount"`
	ProviderAmountMinor    int64               `json:"providerAmountMinor,omitempty"`
	CreatedAtMS            int64               `json:"createdAtMs"`
	ProviderCode           string              `json:"providerCode"`
	UserPaidCurrencyCode   string              `json:"userPaidCurrencyCode"`
	UserPaidAmountMicro    int64               `json:"userPaidAmountMicro"`
	ProviderFeeMicro       int64               `json:"providerFeeMicro"`
	ProviderTaxMicro       int64               `json:"providerTaxMicro"`
	ProviderNetMicro       int64               `json:"providerNetMicro"`
	PaidSyncedAtMS         int64               `json:"paidSyncedAtMs"`
	User                   rechargeBillUserDTO `json:"user"`
	Seller                 rechargeBillUserDTO `json:"seller"`
}

type rechargeBillUserDTO struct {
	UserID             string `json:"userId"`
	DisplayUserID      string `json:"displayUserId"`
	Username           string `json:"username"`
	Avatar             string `json:"avatar"`
	CountryCode        string `json:"countryCode"`
	CountryName        string `json:"countryName"`
	CountryDisplayName string `json:"countryDisplayName"`
}

type rechargeProductDTO struct {
	AppCode           string  `json:"appCode"`
	ProductID         int64   `json:"productId"`
	ProductCode       string  `json:"productCode"`
	ProductName       string  `json:"productName"`
	Description       string  `json:"description"`
	AudienceType      string  `json:"audienceType"`
	Platform          string  `json:"platform"`
	Channel           string  `json:"channel"`
	CurrencyCode      string  `json:"currencyCode"`
	AmountUSDT        string  `json:"amountUsdt"`
	AmountUSDTMicro   int64   `json:"amountUsdtMicro"`
	AmountMicro       int64   `json:"amountMicro"`
	AmountMinor       int64   `json:"amountMinor"`
	CoinAmount        int64   `json:"coinAmount"`
	PolicyVersion     string  `json:"policyVersion"`
	RegionIDs         []int64 `json:"regionIds"`
	ResourceAssetType string  `json:"resourceAssetType"`
	Status            string  `json:"status"`
	Enabled           bool    `json:"enabled"`
	AppVisible        bool    `json:"appVisible"`
	SortOrder         int32   `json:"sortOrder"`
	CreatedByUserID   int64   `json:"createdByUserId"`
	UpdatedByUserID   int64   `json:"updatedByUserId"`
	CreatedAtMS       int64   `json:"createdAtMs"`
	UpdatedAtMS       int64   `json:"updatedAtMs"`
}

type thirdPartyPaymentChannelDTO struct {
	AppCode      string                       `json:"appCode"`
	ProviderCode string                       `json:"providerCode"`
	ProviderName string                       `json:"providerName"`
	Status       string                       `json:"status"`
	SortOrder    int32                        `json:"sortOrder"`
	Methods      []thirdPartyPaymentMethodDTO `json:"methods"`
}

type thirdPartyPaymentMethodDTO struct {
	MethodID          int64  `json:"methodId"`
	AppCode           string `json:"appCode"`
	ProviderCode      string `json:"providerCode"`
	ProviderName      string `json:"providerName"`
	CountryCode       string `json:"countryCode"`
	CountryName       string `json:"countryName"`
	CurrencyCode      string `json:"currencyCode"`
	PayWay            string `json:"payWay"`
	PayType           string `json:"payType"`
	MethodName        string `json:"methodName"`
	LogoURL           string `json:"logoUrl"`
	Status            string `json:"status"`
	USDToCurrencyRate string `json:"usdToCurrencyRate"`
	SortOrder         int32  `json:"sortOrder"`
	CreatedAtMS       int64  `json:"createdAtMs"`
	UpdatedAtMS       int64  `json:"updatedAtMs"`
}

type thirdPartyPaymentMethodSyncDTO struct {
	ProviderCode        string                            `json:"providerCode"`
	ScannedCountryCount int32                             `json:"scannedCountryCount"`
	FetchedCountryCount int32                             `json:"fetchedCountryCount"`
	CreatedCount        int32                             `json:"createdCount"`
	UpdatedCount        int32                             `json:"updatedCount"`
	SkippedCount        int32                             `json:"skippedCount"`
	FailedCountryCount  int                               `json:"failedCountryCount"`
	Issues              []thirdPartyPaymentMethodIssueDTO `json:"issues"`
}

type thirdPartyPaymentMethodIssueDTO struct {
	CountryCode  string `json:"countryCode"`
	CurrencyCode string `json:"currencyCode"`
	Code         string `json:"code"`
	Message      string `json:"message"`
}

type temporaryPaymentLinkDTO struct {
	OrderID             string `json:"orderId"`
	AppCode             string `json:"appCode"`
	RegionID            int64  `json:"regionId"`
	OwnerUserID         uint   `json:"ownerUserId,omitempty"`
	OwnerUsername       string `json:"ownerUsername,omitempty"`
	OwnerName           string `json:"ownerName,omitempty"`
	CommandID           string `json:"commandId,omitempty"`
	AudienceType        string `json:"audienceType"`
	ProductName         string `json:"productName"`
	CoinAmount          int64  `json:"coinAmount"`
	USDMinorAmount      int64  `json:"usdMinorAmount"`
	ProviderCode        string `json:"providerCode"`
	PaymentMethodID     int64  `json:"paymentMethodId"`
	CountryCode         string `json:"countryCode"`
	CurrencyCode        string `json:"currencyCode"`
	ProviderAmountMinor int64  `json:"providerAmountMinor"`
	PayWay              string `json:"payWay"`
	PayType             string `json:"payType"`
	PayURL              string `json:"payUrl"`
	ProviderOrderID     string `json:"providerOrderId"`
	TxHash              string `json:"txHash"`
	Status              string `json:"status"`
	FailureReason       string `json:"failureReason"`
	TransactionID       string `json:"transactionId"`
	CreatedAtMS         int64  `json:"createdAtMs"`
	UpdatedAtMS         int64  `json:"updatedAtMs"`
}

func rechargeBillFromProto(item *walletv1.RechargeBill) rechargeBillDTO {
	if item == nil {
		return rechargeBillDTO{}
	}
	return rechargeBillDTO{
		AppCode:                item.GetAppCode(),
		TransactionID:          item.GetTransactionId(),
		CommandID:              item.GetCommandId(),
		RechargeType:           item.GetRechargeType(),
		Status:                 item.GetStatus(),
		ExternalRef:            item.GetExternalRef(),
		UserID:                 item.GetUserId(),
		SellerUserID:           item.GetSellerUserId(),
		SellerRegionID:         item.GetSellerRegionId(),
		TargetRegionID:         item.GetTargetRegionId(),
		PolicyID:               item.GetPolicyId(),
		PolicyVersion:          item.GetPolicyVersion(),
		CurrencyCode:           item.GetCurrencyCode(),
		CoinAmount:             item.GetCoinAmount(),
		USDMinorAmount:         item.GetUsdMinorAmount(),
		ExchangeCoinAmount:     item.GetExchangeCoinAmount(),
		ExchangeUSDMinorAmount: item.GetExchangeUsdMinorAmount(),
		ProviderAmountMinor:    item.GetProviderAmountMinor(),
		CreatedAtMS:            item.GetCreatedAtMs(),
		ProviderCode:           item.GetProviderCode(),
		UserPaidCurrencyCode:   item.GetUserPaidCurrencyCode(),
		UserPaidAmountMicro:    item.GetUserPaidAmountMicro(),
		ProviderFeeMicro:       item.GetProviderFeeMicro(),
		ProviderTaxMicro:       item.GetProviderTaxMicro(),
		ProviderNetMicro:       item.GetProviderNetMicro(),
		PaidSyncedAtMS:         item.GetPaidSyncedAtMs(),
	}
}

// collectRechargeBillUserIDs 把付款用户和币商用户放进同一个去重列表；后续只查一次 users 表即可覆盖两列展示。
func collectRechargeBillUserIDs(items []rechargeBillDTO) []int64 {
	seen := make(map[int64]struct{}, len(items)*2)
	userIDs := make([]int64, 0, len(items)*2)
	for _, item := range items {
		for _, userID := range []int64{item.UserID, item.SellerUserID} {
			if userID <= 0 {
				continue
			}
			if _, ok := seen[userID]; ok {
				continue
			}
			seen[userID] = struct{}{}
			userIDs = append(userIDs, userID)
		}
	}
	return userIDs
}

// rechargeBillUserOrFallback 保证用户资料缺失时仍返回长 ID，列表和导出不会因为历史用户资料缺失而显示空白。
func rechargeBillUserOrFallback(profile rechargeBillUserDTO, userID int64) rechargeBillUserDTO {
	if userID <= 0 {
		return rechargeBillUserDTO{}
	}
	if profile.UserID != "" {
		return profile
	}
	return rechargeBillUserDTO{UserID: strconv.FormatInt(userID, 10)}
}

// int64Args 将用户 ID 列表转换为 database/sql 可展开的参数切片，避免为 IN 条件拼接原始数值。
func int64Args(values []int64) []any {
	args := make([]any, 0, len(values))
	for _, value := range values {
		args = append(args, value)
	}
	return args
}

// placeholders 只生成固定数量的问号占位符；实际用户 ID 仍通过参数绑定传入，避免 SQL 注入。
func placeholders(count int) string {
	if count <= 0 {
		return ""
	}
	items := make([]string, count)
	for index := range items {
		items[index] = "?"
	}
	return strings.Join(items, ",")
}

func rechargeProductFromProto(item *walletv1.RechargeProduct) rechargeProductDTO {
	if item == nil {
		return rechargeProductDTO{}
	}
	return rechargeProductDTO{
		AppCode:           item.GetAppCode(),
		ProductID:         item.GetProductId(),
		ProductCode:       item.GetProductCode(),
		ProductName:       item.GetProductName(),
		Description:       item.GetDescription(),
		AudienceType:      item.GetAudienceType(),
		Platform:          item.GetPlatform(),
		Channel:           item.GetChannel(),
		CurrencyCode:      item.GetCurrencyCode(),
		AmountUSDT:        formatUSDTMicro(item.GetAmountMicro()),
		AmountUSDTMicro:   item.GetAmountMicro(),
		AmountMicro:       item.GetAmountMicro(),
		AmountMinor:       item.GetAmountMinor(),
		CoinAmount:        item.GetCoinAmount(),
		PolicyVersion:     item.GetPolicyVersion(),
		RegionIDs:         append([]int64(nil), item.GetRegionIds()...),
		ResourceAssetType: item.GetResourceAssetType(),
		Status:            item.GetStatus(),
		Enabled:           item.GetEnabled(),
		AppVisible:        item.GetAppVisible(),
		SortOrder:         item.GetSortOrder(),
		CreatedByUserID:   item.GetCreatedByUserId(),
		UpdatedByUserID:   item.GetUpdatedByUserId(),
		CreatedAtMS:       item.GetCreatedAtMs(),
		UpdatedAtMS:       item.GetUpdatedAtMs(),
	}
}

func thirdPartyPaymentChannelFromProto(item *walletv1.ThirdPartyPaymentChannel) thirdPartyPaymentChannelDTO {
	if item == nil {
		return thirdPartyPaymentChannelDTO{}
	}
	dto := thirdPartyPaymentChannelDTO{
		AppCode:      item.GetAppCode(),
		ProviderCode: item.GetProviderCode(),
		ProviderName: item.GetProviderName(),
		Status:       item.GetStatus(),
		SortOrder:    item.GetSortOrder(),
		Methods:      make([]thirdPartyPaymentMethodDTO, 0, len(item.GetMethods())),
	}
	for _, method := range item.GetMethods() {
		dto.Methods = append(dto.Methods, thirdPartyPaymentMethodFromProto(method))
	}
	return dto
}

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

func thirdPartyPaymentMethodSyncFromProto(item *walletv1.SyncThirdPartyPaymentMethodsResponse) thirdPartyPaymentMethodSyncDTO {
	if item == nil {
		return thirdPartyPaymentMethodSyncDTO{}
	}
	dto := thirdPartyPaymentMethodSyncDTO{
		ProviderCode:        item.GetProviderCode(),
		ScannedCountryCount: item.GetScannedCountryCount(),
		FetchedCountryCount: item.GetFetchedCountryCount(),
		CreatedCount:        item.GetCreatedCount(),
		UpdatedCount:        item.GetUpdatedCount(),
		SkippedCount:        item.GetSkippedCount(),
		FailedCountryCount:  len(item.GetIssues()),
		Issues:              make([]thirdPartyPaymentMethodIssueDTO, 0, len(item.GetIssues())),
	}
	for _, issue := range item.GetIssues() {
		dto.Issues = append(dto.Issues, thirdPartyPaymentMethodIssueDTO{
			CountryCode:  issue.GetCountryCode(),
			CurrencyCode: issue.GetCurrencyCode(),
			Code:         issue.GetCode(),
			Message:      issue.GetMessage(),
		})
	}
	return dto
}

func temporaryPaymentLinkFromProto(item *walletv1.ExternalRechargeOrder) temporaryPaymentLinkDTO {
	if item == nil {
		return temporaryPaymentLinkDTO{}
	}
	return temporaryPaymentLinkDTO{
		OrderID:             item.GetOrderId(),
		AppCode:             item.GetAppCode(),
		AudienceType:        item.GetAudienceType(),
		ProductName:         item.GetProductName(),
		CoinAmount:          item.GetCoinAmount(),
		USDMinorAmount:      item.GetUsdMinorAmount(),
		ProviderCode:        item.GetProviderCode(),
		PaymentMethodID:     item.GetPaymentMethodId(),
		CountryCode:         item.GetCountryCode(),
		CurrencyCode:        item.GetCurrencyCode(),
		ProviderAmountMinor: item.GetProviderAmountMinor(),
		PayWay:              item.GetPayWay(),
		PayType:             item.GetPayType(),
		PayURL:              item.GetPayUrl(),
		ProviderOrderID:     item.GetProviderOrderId(),
		TxHash:              item.GetTxHash(),
		Status:              item.GetStatus(),
		FailureReason:       item.GetFailureReason(),
		TransactionID:       item.GetTransactionId(),
		CreatedAtMS:         item.GetCreatedAtMs(),
		UpdatedAtMS:         item.GetUpdatedAtMs(),
	}
}

func formatUSDTMicro(amount int64) string {
	if amount <= 0 {
		return "0"
	}
	whole := amount / usdtMicroUnit
	fraction := amount % usdtMicroUnit
	if fraction == 0 {
		return strconv.FormatInt(whole, 10)
	}
	return fmt.Sprintf("%d.%s", whole, strings.TrimRight(fmt.Sprintf("%06d", fraction), "0"))
}
