package wallet

import (
	"context"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	"hyapp/services/wallet-service/internal/domain/ledger"
	"strings"
)

const temporaryProviderMemberID int64 = 1

// CreateTemporaryRechargeOrder 创建运营临时收款订单；它只校验支付方式和金额，不读取充值商品。
func (s *Service) CreateTemporaryRechargeOrder(ctx context.Context, command ledger.CreateTemporaryExternalRechargeOrderCommand) (ledger.ExternalRechargeOrder, error) {
	command = normalizeCreateTemporaryExternalRechargeOrderCommand(command)
	if err := validateCreateTemporaryExternalRechargeOrderCommand(command); err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	if s.repository == nil {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, command.AppCode)

	switch command.ProviderCode {
	case ledger.PaymentProviderMifaPay:
		return s.createTemporaryMifaPayRechargeOrder(ctx, command)
	case ledger.PaymentProviderV5Pay:
		return s.createTemporaryV5PayRechargeOrder(ctx, command)
	default:
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.InvalidArgument, "payment provider is invalid")
	}
}

func (s *Service) createTemporaryMifaPayRechargeOrder(ctx context.Context, command ledger.CreateTemporaryExternalRechargeOrderCommand) (ledger.ExternalRechargeOrder, error) {
	if s.mifaPay == nil {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "mifapay client is not configured")
	}
	method, err := s.sharedPaymentMethod(ctx, command.AppCode, ledger.PaymentProviderMifaPay, command.PaymentMethodID)
	if err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	if err := validateMifaPayMethodForTemporaryOrder(method, command); err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	// 临时链接没有商品档位，三方实付金额只能由运营输入的 USD 金额和支付方式汇率即时换算；订单快照会保存换算结果。
	providerAmountMinor, err := calculateMifaPayProviderAmountMinor(command.USDMinorAmount, method.USDToCurrencyRate, method.CurrencyCode)
	if err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	// 先写 pending 订单再调三方，保证三方回调先于 HTTP 响应返回时也能按 order_id 找到本地订单。
	order, err := s.repository.CreateTemporaryExternalRechargeOrder(ctx, command, method, providerAmountMinor)
	if err != nil || order.IdempotentReplay {
		return order, err
	}
	// notify_url 是支付状态闭环的必要条件；缺配置时把订单打失败，避免运营拿到一个无法回调核销的链接。
	notifyURL := firstNonEmpty(command.NotifyURL, s.externalRecharge.MifaPayNotifyURL)
	returnURL := firstNonEmpty(command.ReturnURL, s.externalRecharge.MifaPayReturnURL)
	if notifyURL == "" {
		_, _ = s.repository.MarkExternalRechargeOrderFailed(ctx, command.AppCode, order.OrderID, "mifapay notify_url is not configured", "")
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "mifapay notify_url is not configured")
	}
	resp, err := s.mifaPay.CreateOrder(ctx, MifaPayCreateOrderRequest{
		OrderID:             order.OrderID,
		TargetUserID:        temporaryProviderMemberID,
		ProductName:         order.ProductName,
		CoinAmount:          order.CoinAmount,
		AmountMinor:         order.USDMinorAmount,
		CurrencyCode:        order.CurrencyCode,
		CountryCode:         order.CountryCode,
		PayWay:              order.PayWay,
		PayType:             order.PayType,
		NotifyURL:           notifyURL,
		ReturnURL:           mifaPayProviderReturnURL(returnURL),
		ClientIP:            command.ClientIP,
		Language:            command.Language,
		ProviderAmountMinor: providerAmountMinor,
		PayerName:           command.PayerName,
		PayerAccount:        command.PayerAccount,
		PayerEmail:          command.PayerEmail,
	})
	if err != nil {
		_, _ = s.repository.MarkExternalRechargeOrderFailed(ctx, command.AppCode, order.OrderID, err.Error(), mifaPayProviderPayloadJSON(err))
		return ledger.ExternalRechargeOrder{}, normalizeMifaPayOrderError(err)
	}
	return s.repository.MarkExternalRechargeOrderRedirected(ctx, command.AppCode, order.OrderID, resp.ProviderOrderID, resp.PayURL, resp.RawJSON)
}

func (s *Service) createTemporaryV5PayRechargeOrder(ctx context.Context, command ledger.CreateTemporaryExternalRechargeOrderCommand) (ledger.ExternalRechargeOrder, error) {
	if s.v5Pay == nil {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "v5pay client is not configured")
	}
	method, err := s.sharedPaymentMethod(ctx, command.AppCode, ledger.PaymentProviderV5Pay, command.PaymentMethodID)
	if err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	if err := validateV5PayMethodForTemporaryOrder(method, command); err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	// V5Pay 同样以支付方式汇率换算本币金额；临时订单不读取 wallet_recharge_products，避免误套正式商品受众。
	providerAmountMinor, err := calculateProviderAmountMinor(command.USDMinorAmount, method.USDToCurrencyRate, method.CurrencyCode)
	if err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	// 本地订单先落库，三方创建失败再转 failed；这样人工排查时能看到每次临时建链尝试。
	order, err := s.repository.CreateTemporaryExternalRechargeOrder(ctx, command, method, providerAmountMinor)
	if err != nil || order.IdempotentReplay {
		return order, err
	}
	notifyURL := firstNonEmpty(command.NotifyURL, s.externalRecharge.V5PayNotifyURL)
	returnURL := firstNonEmpty(command.ReturnURL, s.externalRecharge.V5PayReturnURL)
	if notifyURL == "" {
		_, _ = s.repository.MarkExternalRechargeOrderFailed(ctx, command.AppCode, order.OrderID, "v5pay notify_url is not configured", "")
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "v5pay notify_url is not configured")
	}
	resp, err := s.v5Pay.CreateOrder(ctx, V5PayCreateOrderRequest{
		OrderID:             order.OrderID,
		TargetUserID:        temporaryProviderMemberID,
		ProductName:         order.ProductName,
		CoinAmount:          order.CoinAmount,
		AmountMinor:         order.USDMinorAmount,
		CurrencyCode:        order.CurrencyCode,
		CountryCode:         order.CountryCode,
		ProductType:         order.PayType,
		NotifyURL:           notifyURL,
		ReturnURL:           v5PayProviderReturnURL(returnURL),
		Language:            command.Language,
		ProviderAmountMinor: providerAmountMinor,
		PayerName:           command.PayerName,
		PayerAccount:        command.PayerAccount,
		PayerEmail:          command.PayerEmail,
	})
	if err != nil {
		_, _ = s.repository.MarkExternalRechargeOrderFailed(ctx, command.AppCode, order.OrderID, err.Error(), providerPayloadJSON(err))
		return ledger.ExternalRechargeOrder{}, normalizeExternalProviderOrderError(err, "v5pay")
	}
	return s.repository.MarkExternalRechargeOrderRedirected(ctx, command.AppCode, order.OrderID, resp.ProviderOrderID, resp.PayURL, resp.RawJSON)
}

func (s *Service) GetTemporaryRechargeOrder(ctx context.Context, appCode string, orderID string) (ledger.ExternalRechargeOrder, error) {
	if s.repository == nil {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	ctx = appcode.WithContext(ctx, appcode.Normalize(appCode))
	order, err := s.repository.GetExternalRechargeOrder(ctx, appcode.FromContext(ctx), strings.TrimSpace(orderID))
	if err != nil {
		return ledger.ExternalRechargeOrder{}, err
	}
	if !isTemporaryExternalRechargeOrder(order) {
		return ledger.ExternalRechargeOrder{}, xerr.New(xerr.NotFound, "temporary recharge order not found")
	}
	// 单笔查询被 H5 验证框和 admin 页面共用；这里主动刷新三方状态，但刷新失败不盖掉本地快照，避免短暂上游错误让后台看不到订单。
	if refreshed, err := s.refreshMifaPayRechargeOrder(ctx, order); err == nil {
		order = refreshed
	}
	if refreshed, err := s.refreshV5PayRechargeOrder(ctx, order); err == nil {
		order = refreshed
	}
	return order, nil
}

func (s *Service) ListTemporaryRechargeOrders(ctx context.Context, query ledger.ListTemporaryExternalRechargeOrdersQuery) ([]ledger.ExternalRechargeOrder, int64, error) {
	if s.repository == nil {
		return nil, 0, xerr.New(xerr.Unavailable, "wallet repository is not configured")
	}
	query.AppCode = appcode.Normalize(query.AppCode)
	query.ProviderCode = ledger.NormalizePaymentProvider(query.ProviderCode)
	query.Status = strings.TrimSpace(query.Status)
	query.Keyword = strings.TrimSpace(query.Keyword)
	ctx = appcode.WithContext(ctx, query.AppCode)
	return s.repository.ListTemporaryExternalRechargeOrders(ctx, query)
}

func normalizeCreateTemporaryExternalRechargeOrderCommand(command ledger.CreateTemporaryExternalRechargeOrderCommand) ledger.CreateTemporaryExternalRechargeOrderCommand {
	command.AppCode = appcode.Normalize(command.AppCode)
	command.CommandID = strings.TrimSpace(command.CommandID)
	command.ProviderCode = ledger.NormalizePaymentProvider(command.ProviderCode)
	command.ReturnURL = strings.TrimSpace(command.ReturnURL)
	command.NotifyURL = strings.TrimSpace(command.NotifyURL)
	command.ClientIP = strings.TrimSpace(command.ClientIP)
	command.Language = normalizeMifaPayLanguage(command.Language)
	command.PayerName = strings.TrimSpace(command.PayerName)
	command.PayerAccount = strings.TrimSpace(command.PayerAccount)
	command.PayerEmail = strings.TrimSpace(command.PayerEmail)
	return command
}

func validateCreateTemporaryExternalRechargeOrderCommand(command ledger.CreateTemporaryExternalRechargeOrderCommand) error {
	if command.CommandID == "" || command.USDMinorAmount <= 0 || command.PaymentMethodID <= 0 {
		return xerr.New(xerr.InvalidArgument, "temporary recharge command is incomplete")
	}
	// 临时支付链接只记录三方收款事实，不会触发用户金币入账；负数没有业务含义，0 用于表达“不发币”。
	if command.CoinAmount < 0 {
		return xerr.New(xerr.InvalidArgument, "temporary recharge coin amount is invalid")
	}
	if command.ProviderCode == "" {
		return xerr.New(xerr.InvalidArgument, "temporary recharge provider is invalid")
	}
	if len(command.CommandID) > 128 {
		return xerr.New(xerr.InvalidArgument, "command_id is too long")
	}
	return nil
}

func validateMifaPayMethodForTemporaryOrder(method ledger.ThirdPartyPaymentMethod, command ledger.CreateTemporaryExternalRechargeOrderCommand) error {
	if method.ProviderCode != ledger.PaymentProviderMifaPay || method.Status != ledger.ThirdPartyPaymentStatusActive || method.MethodID != command.PaymentMethodID {
		return xerr.New(xerr.Conflict, "mifapay method is not active")
	}
	if _, err := calculateMifaPayProviderAmountMinor(command.USDMinorAmount, method.USDToCurrencyRate, method.CurrencyCode); err != nil {
		return err
	}
	return nil
}

func validateV5PayMethodForTemporaryOrder(method ledger.ThirdPartyPaymentMethod, command ledger.CreateTemporaryExternalRechargeOrderCommand) error {
	if method.ProviderCode != ledger.PaymentProviderV5Pay || method.Status != ledger.ThirdPartyPaymentStatusActive || method.MethodID != command.PaymentMethodID {
		return xerr.New(xerr.Conflict, "v5pay method is not active")
	}
	if strings.TrimSpace(method.PayType) == "" {
		return xerr.New(xerr.InvalidArgument, "v5pay product type is required")
	}
	if _, err := calculateProviderAmountMinor(command.USDMinorAmount, method.USDToCurrencyRate, method.CurrencyCode); err != nil {
		return err
	}
	return nil
}

func isTemporaryExternalRechargeOrder(order ledger.ExternalRechargeOrder) bool {
	return order.AudienceType == ledger.RechargeAudienceTemporary || strings.HasPrefix(order.CommandID, "h5_temporary_recharge:")
}
