package client_order

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/fuiou"
	"github.com/break/junhong_cmp_fiber/pkg/wechat"
)

// PaymentProvider C 端支付提供者接口。
// 抽象不同支付渠道（微信、富友等）的预下单能力。
type PaymentProvider interface {
	// CreateJSAPIPayment 创建 JSAPI 预下单，返回前端调起支付的参数。
	CreateJSAPIPayment(ctx context.Context, orderNo, description, openID string, amount int) (*PaymentResult, error)
}

// PaymentResult 预下单结果，前端用这些参数调起支付。
type PaymentResult struct {
	AppID     string
	TimeStamp string
	NonceStr  string
	Package   string
	SignType  string
	PaySign   string
}

// wechatPaymentProvider 微信支付适配器。
type wechatPaymentProvider struct {
	paymentService *wechat.PaymentService
}

// CreateJSAPIPayment 创建微信 JSAPI 预下单。
func (w *wechatPaymentProvider) CreateJSAPIPayment(ctx context.Context, orderNo, description, openID string, amount int) (*PaymentResult, error) {
	result, err := w.paymentService.CreateJSAPIOrder(ctx, orderNo, description, openID, amount)
	if err != nil {
		return nil, err
	}
	return extractPaymentResult(result.PayConfig), nil
}

// wechatV2PaymentProvider 微信支付 v2 适配器。
type wechatV2PaymentProvider struct {
	paymentService *wechat.PaymentV2Service
}

// CreateJSAPIPayment 创建微信 v2 JSAPI 预下单。
func (w *wechatV2PaymentProvider) CreateJSAPIPayment(ctx context.Context, orderNo, description, openID string, amount int) (*PaymentResult, error) {
	result, err := w.paymentService.CreateJSAPIOrder(ctx, orderNo, description, openID, amount)
	if err != nil {
		return nil, err
	}
	return extractPaymentResult(result.PayConfig), nil
}

// fuiouPaymentProvider 富友支付适配器。
type fuiouPaymentProvider struct {
	client    *fuiou.Client
	appID     string
	tradeType string
}

// CreateJSAPIPayment 创建富友 JSAPI 预下单。
func (f *fuiouPaymentProvider) CreateJSAPIPayment(ctx context.Context, orderNo, description, openID string, amount int) (*PaymentResult, error) {
	amountStr := strconv.Itoa(amount)
	resp, err := f.client.WxPreCreate(orderNo, amountStr, description, fuiou.GetServerIP(), f.tradeType, f.appID, openID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeFuiouPayFailed, err, "富友预下单失败")
	}

	return &PaymentResult{
		AppID:     resp.SdkAppid,
		TimeStamp: resp.SdkTimestamp,
		NonceStr:  resp.SdkNoncestr,
		Package:   resp.SdkPackage,
		SignType:  resp.SdkSigntype,
		PaySign:   resp.SdkPaysign,
	}, nil
}

// newPaymentProvider 根据支付配置创建支付提供者。
// appType: "official_account" 或 "miniapp"
func (s *Service) newPaymentProvider(config *model.WechatConfig, appID, appType string) (PaymentProvider, error) {
	switch config.ProviderType {
	case model.ProviderTypeFuiou:
		tradeType := "JSAPI"
		if appType == "miniapp" {
			tradeType = "LETPAY"
		}
		client, err := fuiou.NewClient(
			config.FyInsCd,
			config.FyMchntCd,
			config.FyTermID,
			config.FyAPIURL,
			config.FyNotifyURL,
			config.FyPrivateKey,
			config.FyPublicKey,
			s.logger,
		)
		if err != nil {
			return nil, errors.Wrap(errors.CodeFuiouPayFailed, err, "创建富友客户端失败")
		}
		return &fuiouPaymentProvider{client: client, appID: appID, tradeType: tradeType}, nil
	case model.ProviderTypeWechatV2:
		svc, err := wechat.NewPaymentV2ServiceFromConfig(config, appID, s.logger)
		if err != nil {
			return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "创建微信支付 v2 应用失败")
		}
		return &wechatV2PaymentProvider{paymentService: svc}, nil
	default:
		cache := wechat.NewRedisCache(s.redis)
		paymentApp, err := wechat.NewPaymentAppFromConfig(config, appID, cache, s.logger)
		if err != nil {
			return nil, errors.Wrap(errors.CodeWechatPayFailed, err, "创建微信支付应用失败")
		}
		return &wechatPaymentProvider{paymentService: wechat.NewPaymentService(paymentApp, s.logger)}, nil
	}
}

// buildClientPayConfigFromResult 将预下单结果转换为客户端支付参数。
func buildClientPayConfigFromResult(result *PaymentResult) *dto.ClientPayConfig {
	if result == nil {
		result = &PaymentResult{}
	}

	return &dto.ClientPayConfig{
		AppID:      result.AppID,
		Timestamp:  result.TimeStamp,
		NonceStr:   result.NonceStr,
		PackageVal: result.Package,
		SignType:   result.SignType,
		PaySign:    result.PaySign,
	}
}

// extractPaymentResult 从微信 SDK 的 map 配置中提取统一支付参数。
// 支持两种类型：
// - map[string]any：V1 标准微信 SDK（PowerWeChat）返回
// - map[string]string：V2 自建实现返回
func extractPaymentResult(payConfig any) *PaymentResult {
	if configMap, ok := payConfig.(map[string]any); ok {
		return &PaymentResult{
			AppID:     payFirstNonEmpty(payStringFromAny(configMap["appId"])),
			TimeStamp: payFirstNonEmpty(payStringFromAny(configMap["timeStamp"]), payStringFromAny(configMap["timestamp"])),
			NonceStr:  payStringFromAny(configMap["nonceStr"]),
			Package:   payStringFromAny(configMap["package"]),
			SignType:  payStringFromAny(configMap["signType"]),
			PaySign:   payStringFromAny(configMap["paySign"]),
		}
	}
	// V2 自建实现返回 map[string]string，原 map[string]any 断言会失败导致字段全空
	if configMap, ok := payConfig.(map[string]string); ok {
		return &PaymentResult{
			AppID:     configMap["appId"],
			TimeStamp: payFirstNonEmpty(configMap["timeStamp"], configMap["timestamp"]),
			NonceStr:  configMap["nonceStr"],
			Package:   configMap["package"],
			SignType:  configMap["signType"],
			PaySign:   configMap["paySign"],
		}
	}
	return &PaymentResult{}
}

func payStringFromAny(value any) string {
	if value == nil {
		return ""
	}
	return fmt.Sprint(value)
}

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