package wallet

import (
	"context"
	"encoding/json"
	"strings"

	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
)

type v5PayProductSyncCountry struct {
	CountryCode  string
	CountryName  string
	CurrencyCode string
}

var v5PayProductSyncCountries = []v5PayProductSyncCountry{
	{CountryCode: "PH", CountryName: "Philippines", CurrencyCode: "PHP"},
	{CountryCode: "SA", CountryName: "Saudi Arabia", CurrencyCode: "SAR"},
	{CountryCode: "EG", CountryName: "Egypt", CurrencyCode: "EGP"},
	{CountryCode: "AE", CountryName: "United Arab Emirates", CurrencyCode: "AED"},
	{CountryCode: "ID", CountryName: "Indonesia", CurrencyCode: "IDR"},
	{CountryCode: "IN", CountryName: "India", CurrencyCode: "INR"},
	{CountryCode: "PK", CountryName: "Pakistan", CurrencyCode: "PKR"},
	{CountryCode: "TR", CountryName: "Turkey", CurrencyCode: "TRY"},
	{CountryCode: "MX", CountryName: "Mexico", CurrencyCode: "MXN"},
	{CountryCode: "VN", CountryName: "Vietnam", CurrencyCode: "VND"},
	{CountryCode: "BR", CountryName: "Brazil", CurrencyCode: "BRL"},
}

// SyncThirdPartyPaymentMethods 从支付商实时拉取已开通方式并落本地配置表；当前只有 V5Pay 提供 productTypes 能力。
func (s *Service) SyncThirdPartyPaymentMethods(ctx context.Context, command ledger.ThirdPartyPaymentMethodSyncCommand) (ledger.ThirdPartyPaymentMethodSyncResult, error) {
	command.AppCode = appcode.Normalize(command.AppCode)
	command.ProviderCode = ledger.NormalizePaymentProvider(command.ProviderCode)
	result := ledger.ThirdPartyPaymentMethodSyncResult{ProviderCode: command.ProviderCode}
	if command.ProviderCode == "" {
		command.ProviderCode = ledger.PaymentProviderV5Pay
		result.ProviderCode = command.ProviderCode
	}
	if command.OperatorUserID <= 0 {
		return result, xerr.New(xerr.InvalidArgument, "payment method sync operator is required")
	}
	if command.ProviderCode != ledger.PaymentProviderV5Pay {
		return result, xerr.New(xerr.InvalidArgument, "payment provider does not support method sync")
	}
	if s.repository == nil {
		return result, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	if s.v5Pay == nil {
		return result, xerr.New(xerr.Unavailable, "v5pay client is not configured")
	}

	ctx = appcode.WithContext(ctx, command.AppCode)
	methods, skipped, issues := s.fetchV5PayPaymentMethodsForSync(ctx)
	result.ScannedCountryCount = len(v5PayProductSyncCountries)
	result.FetchedCountryCount = len(v5PayProductSyncCountries) - len(issues)
	result.SkippedCount = skipped
	result.Issues = issues
	if len(methods) == 0 {
		return result, nil
	}
	created, updated, err := s.repository.UpsertThirdPartyPaymentMethods(ctx, command.AppCode, ledger.PaymentProviderV5Pay, methods)
	if err != nil {
		return result, err
	}
	result.CreatedCount = created
	result.UpdatedCount = updated
	return result, nil
}

func (s *Service) fetchV5PayPaymentMethodsForSync(ctx context.Context) ([]ledger.ThirdPartyPaymentMethod, int, []ledger.ThirdPartyPaymentMethodSyncIssue) {
	methods := make([]ledger.ThirdPartyPaymentMethod, 0)
	issues := make([]ledger.ThirdPartyPaymentMethodSyncIssue, 0)
	skipped := 0
	seen := map[string]struct{}{}
	for countryIndex, country := range v5PayProductSyncCountries {
		// V5Pay 的 productTypes 接口必须逐国家/币种查询，没有“全部国家”接口；候选集覆盖当前种子国家和后台已开通截图里的新增国家。
		products, err := s.v5Pay.ListProductTypes(ctx, V5PayProductTypesRequest{
			CountryCode:  country.CountryCode,
			CurrencyCode: country.CurrencyCode,
		})
		if err != nil {
			issues = append(issues, v5PayProductSyncIssue(country, err))
			continue
		}
		// 国家数组与支付商产品数组都是默认展示顺序；分桶后从高到低赋权，确保同步不会反转任一层级。
		countrySortBase := int32((len(v5PayProductSyncCountries) - countryIndex) * 1000)
		for index, product := range products.PayinList {
			method, ok := v5PayProductToPaymentMethod(country, product, countrySortBase, index, len(products.PayinList))
			if !ok {
				skipped++
				continue
			}
			key := thirdPartyPaymentMethodSyncKey(method)
			if _, exists := seen[key]; exists {
				skipped++
				continue
			}
			seen[key] = struct{}{}
			methods = append(methods, method)
		}
	}
	return methods, skipped, issues
}

func v5PayProductToPaymentMethod(country v5PayProductSyncCountry, product V5PayProductType, countrySortBase int32, index int, total int) (ledger.ThirdPartyPaymentMethod, bool) {
	productType := strings.ToUpper(strings.TrimSpace(product.ProductType))
	currency := strings.ToUpper(strings.TrimSpace(product.Currency))
	if currency == "" {
		currency = country.CurrencyCode
	}
	if productType == "" || product.SupportCashierMode == 0 || !strings.EqualFold(currency, country.CurrencyCode) {
		return ledger.ThirdPartyPaymentMethod{}, false
	}
	methodName := strings.TrimSpace(product.ProductName)
	if methodName == "" {
		methodName = productType
	}
	return ledger.ThirdPartyPaymentMethod{
		ProviderCode:      ledger.PaymentProviderV5Pay,
		CountryCode:       country.CountryCode,
		CountryName:       country.CountryName,
		CurrencyCode:      currency,
		PayWay:            v5PayProductPayWay(product),
		PayType:           productType,
		MethodName:        methodName,
		LogoURL:           strings.TrimSpace(product.Logo),
		Status:            ledger.ThirdPartyPaymentStatusActive,
		USDToCurrencyRate: "0",
		SortOrder:         countrySortBase + int32(total-index)*10,
	}, true
}

func v5PayProductPayWay(product V5PayProductType) string {
	if value := normalizeV5PayProductMode(product.ProductMode); value != "" {
		return value
	}
	value := strings.ToUpper(strings.TrimSpace(product.ProductType + " " + product.ProductName + " " + product.Description))
	switch {
	case strings.Contains(value, "CARD"):
		return "Card"
	case strings.Contains(value, "QR"):
		return "QR"
	case strings.Contains(value, "BANK") || strings.Contains(value, "TRANSFER") || strings.Contains(value, "_VA") || strings.Contains(value, "_ONLINE"):
		return "BankTransfer"
	case strings.Contains(value, "UPI"):
		return "UPI"
	case strings.Contains(value, "BILL"):
		return "Billing"
	default:
		return "Ewallet"
	}
}

func normalizeV5PayProductMode(value string) string {
	mode := strings.ReplaceAll(strings.TrimSpace(value), "_", " ")
	mode = strings.ReplaceAll(mode, "-", " ")
	switch strings.ToLower(strings.ReplaceAll(mode, " ", "")) {
	case "":
		return ""
	case "card":
		return "Card"
	case "bank", "banktransfer", "bankonline", "onlinebanking":
		return "BankTransfer"
	case "ewallet", "wallet":
		return "Ewallet"
	case "qr":
		return "QR"
	case "upi":
		return "UPI"
	case "billing", "bill":
		return "Billing"
	default:
		return strings.TrimSpace(value)
	}
}

func v5PayProductSyncIssue(country v5PayProductSyncCountry, err error) ledger.ThirdPartyPaymentMethodSyncIssue {
	issue := ledger.ThirdPartyPaymentMethodSyncIssue{
		CountryCode:  country.CountryCode,
		CurrencyCode: country.CurrencyCode,
		Code:         "REQUEST_FAILED",
		Message:      err.Error(),
	}
	if raw := providerPayloadJSON(err); raw != "" {
		var payload struct {
			Code    string `json:"code"`
			Message string `json:"message"`
		}
		if json.Unmarshal([]byte(raw), &payload) == nil {
			if strings.TrimSpace(payload.Code) != "" {
				issue.Code = strings.TrimSpace(payload.Code)
			}
			if strings.TrimSpace(payload.Message) != "" {
				issue.Message = strings.TrimSpace(payload.Message)
			}
		}
	}
	return issue
}

func thirdPartyPaymentMethodSyncKey(method ledger.ThirdPartyPaymentMethod) string {
	return strings.ToUpper(strings.TrimSpace(method.CountryCode)) + "|" +
		strings.TrimSpace(method.PayWay) + "|" +
		strings.ToUpper(strings.TrimSpace(method.PayType))
}
