package wechat

import (
	"context"
	"net/http"

	"github.com/ArtisanCloud/PowerWeChat/v3/src/kernel/models"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/payment"
	"github.com/ArtisanCloud/PowerWeChat/v3/src/payment/notify/request"
	orderRequest "github.com/ArtisanCloud/PowerWeChat/v3/src/payment/order/request"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"go.uber.org/zap"
)

// PaymentService 微信支付服务实现
type PaymentService struct {
	app    *payment.Payment
	logger *zap.Logger
}

// NewPaymentService 创建微信支付服务
func NewPaymentService(app *payment.Payment, logger *zap.Logger) *PaymentService {
	return &PaymentService{
		app:    app,
		logger: logger,
	}
}

// JSAPIPayResult JSAPI 支付结果
type JSAPIPayResult struct {
	PrepayID  string      `json:"prepay_id"`
	PayConfig interface{} `json:"pay_config"`
}

// H5PayResult H5 支付结果
type H5PayResult struct {
	H5URL string `json:"h5_url"`
}

// OrderInfo 订单信息
type OrderInfo struct {
	TransactionID  string `json:"transaction_id"`
	OutTradeNo     string `json:"out_trade_no"`
	TradeState     string `json:"trade_state"`
	TradeStateDesc string `json:"trade_state_desc"`
	SuccessTime    string `json:"success_time"`
	TradeType      string `json:"trade_type"`
	BankType       string `json:"bank_type"`
	Attach         string `json:"attach"`
	PayerOpenID    string `json:"payer_openid"`
	TotalAmount    int64  `json:"total_amount"`
	PayerTotal     int64  `json:"payer_total"`
	Currency       string `json:"currency"`
}

// PaymentNotifyResult 支付通知结果
type PaymentNotifyResult struct {
	TransactionID string `json:"transaction_id"`
	OutTradeNo    string `json:"out_trade_no"`
	TradeState    string `json:"trade_state"`
	SuccessTime   string `json:"success_time"`
	PayerOpenID   string `json:"payer_openid"`
	TotalAmount   int64  `json:"total_amount"`
	Attach        string `json:"attach"`
}

// CreateJSAPIOrder 创建 JSAPI 支付订单
func (s *PaymentService) CreateJSAPIOrder(ctx context.Context, orderNo, description, openID string, amount int) (*JSAPIPayResult, error) {
	if orderNo == "" || openID == "" || amount <= 0 {
		return nil, errors.New(errors.CodeInvalidParam, "订单号、OpenID 和金额不能为空")
	}

	resp, err := s.app.Order.JSAPITransaction(ctx, &orderRequest.RequestJSAPIPrepay{
		Description: description,
		OutTradeNo:  orderNo,
		Amount: &orderRequest.JSAPIAmount{
			Total:    amount,
			Currency: "CNY",
		},
		Payer: &orderRequest.JSAPIPayer{
			OpenID: openID,
		},
	})

	if err != nil {
		s.logger.Error("创建 JSAPI 支付订单失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err)
	}

	if resp == nil || resp.PrepayID == "" {
		s.logger.Error("创建 JSAPI 支付订单失败：空 PrepayID", zap.String("order_no", orderNo))
		return nil, errors.New(errors.CodeWechatPayFailed, "创建支付订单失败")
	}

	payConfig, err := s.app.JSSDK.BridgeConfig(resp.PrepayID, false)
	if err != nil {
		s.logger.Error("生成支付配置失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err)
	}

	s.logger.Info("创建 JSAPI 支付订单成功",
		zap.String("order_no", orderNo),
		zap.String("prepay_id", resp.PrepayID),
	)

	return &JSAPIPayResult{
		PrepayID:  resp.PrepayID,
		PayConfig: payConfig,
	}, nil
}

// CreateH5Order 创建 H5 支付订单
func (s *PaymentService) CreateH5Order(ctx context.Context, orderNo, description string, amount int, sceneInfo *H5SceneInfo) (*H5PayResult, error) {
	if orderNo == "" || amount <= 0 {
		return nil, errors.New(errors.CodeInvalidParam, "订单号和金额不能为空")
	}

	req := &orderRequest.RequestH5Prepay{
		Description: description,
		OutTradeNo:  orderNo,
		Amount: &orderRequest.H5Amount{
			Total:    amount,
			Currency: "CNY",
		},
	}

	if sceneInfo != nil {
		req.SceneInfo = &orderRequest.H5SceneInfo{
			PayerClientIP: sceneInfo.PayerClientIP,
			H5Info: &orderRequest.H5H5Info{
				Type: sceneInfo.H5Type,
			},
		}
	}

	resp, err := s.app.Order.TransactionH5(ctx, req)
	if err != nil {
		s.logger.Error("创建 H5 支付订单失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err)
	}

	if resp == nil || resp.H5URL == "" {
		s.logger.Error("创建 H5 支付订单失败：空 H5URL", zap.String("order_no", orderNo))
		return nil, errors.New(errors.CodeWechatPayFailed, "创建 H5 支付订单失败")
	}

	s.logger.Info("创建 H5 支付订单成功",
		zap.String("order_no", orderNo),
		zap.String("h5_url", resp.H5URL),
	)

	return &H5PayResult{
		H5URL: resp.H5URL,
	}, nil
}

// H5SceneInfo H5 支付场景信息
type H5SceneInfo struct {
	PayerClientIP string `json:"payer_client_ip"`
	H5Type        string `json:"h5_type"`
}

// QueryOrder 查询订单
func (s *PaymentService) QueryOrder(ctx context.Context, orderNo string) (*OrderInfo, error) {
	if orderNo == "" {
		return nil, errors.New(errors.CodeInvalidParam, "订单号不能为空")
	}

	resp, err := s.app.Order.QueryByOutTradeNumber(ctx, orderNo)
	if err != nil {
		s.logger.Error("查询订单失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return nil, errors.Wrap(errors.CodeWechatPayFailed, err)
	}

	if resp == nil {
		return nil, errors.New(errors.CodeNotFound, "订单不存在")
	}

	orderInfo := &OrderInfo{
		TransactionID:  resp.TransactionID,
		OutTradeNo:     resp.OutTradeNo,
		TradeState:     resp.TradeState,
		TradeStateDesc: resp.TradeStateDesc,
		SuccessTime:    resp.SuccessTime,
		TradeType:      resp.TradeType,
		BankType:       resp.BankType,
		Attach:         resp.Attach,
	}

	if resp.Amount != nil {
		orderInfo.TotalAmount = resp.Amount.Total
		orderInfo.PayerTotal = resp.Amount.PayerTotal
		orderInfo.Currency = resp.Amount.Currency
	}

	if resp.Payer != nil {
		orderInfo.PayerOpenID = resp.Payer.OpenID
	}

	s.logger.Debug("查询订单成功",
		zap.String("order_no", orderNo),
		zap.String("trade_state", resp.TradeState),
	)

	return orderInfo, nil
}

// CloseOrder 关闭订单
func (s *PaymentService) CloseOrder(ctx context.Context, orderNo string) error {
	if orderNo == "" {
		return errors.New(errors.CodeInvalidParam, "订单号不能为空")
	}

	_, err := s.app.Order.Close(ctx, orderNo)
	if err != nil {
		s.logger.Error("关闭订单失败",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
		return errors.Wrap(errors.CodeWechatPayFailed, err)
	}

	s.logger.Info("关闭订单成功", zap.String("order_no", orderNo))
	return nil
}

// PaymentNotifyCallback 支付通知回调函数
type PaymentNotifyCallback func(result *PaymentNotifyResult) error

// HandlePaymentNotify 处理支付回调通知
func (s *PaymentService) HandlePaymentNotify(r *http.Request, callback PaymentNotifyCallback) (*http.Response, error) {
	return s.app.HandlePaidNotify(r, func(notify *request.RequestNotify, transaction *models.Transaction, fail func(message string)) interface{} {
		if transaction == nil {
			s.logger.Error("支付通知数据为空")
			fail("支付通知数据为空")
			return nil
		}

		result := &PaymentNotifyResult{
			OutTradeNo:  transaction.OutTradeNo,
			TradeState:  transaction.TradeState,
			SuccessTime: transaction.SuccessTime,
			Attach:      transaction.Attach,
		}

		result.TransactionID = transaction.TransactionID
		if transaction.Payer != nil {
			result.PayerOpenID = transaction.Payer.OpenID
		}
		if transaction.Amount != nil {
			result.TotalAmount = transaction.Amount.Total
		}

		if err := callback(result); err != nil {
			s.logger.Error("处理支付通知回调失败",
				zap.String("out_trade_no", result.OutTradeNo),
				zap.Error(err),
			)
			fail(err.Error())
			return nil
		}

		s.logger.Info("支付通知处理成功",
			zap.String("out_trade_no", result.OutTradeNo),
			zap.String("transaction_id", result.TransactionID),
		)

		return true
	})
}
