package payment

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/perfect-panel/server/pkg/payment/stripe"
	"gorm.io/gorm"

	"github.com/perfect-panel/server/pkg/random"

	paymentModel "github.com/perfect-panel/server/internal/model/payment"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/internal/types"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/payment"
	"github.com/perfect-panel/server/pkg/tool"
	"github.com/perfect-panel/server/pkg/xerr"
	"github.com/pkg/errors"
)

type CreatePaymentMethodLogic struct {
	logger.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// NewCreatePaymentMethodLogic Create Payment Method
func NewCreatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePaymentMethodLogic {
	return &CreatePaymentMethodLogic{
		Logger: logger.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentMethodRequest) (resp *types.PaymentConfig, err error) {
	platformType := payment.ParsePlatform(req.Platform)
	if platformType == payment.UNSUPPORTED {
		l.Errorw("unsupported payment platform", logger.Field("mark", req.Platform))
		return nil, errors.Wrapf(xerr.NewErrCodeMsg(400, "UNSUPPORTED_PAYMENT_PLATFORM"), "unsupported payment platform: %s", req.Platform)
	}
	req.Platform = platformType.String()
	config := parsePaymentPlatformConfig(l.ctx, platformType, req.Config)
	var paymentMethod = &paymentModel.Payment{
		Name:        req.Name,
		Platform:    req.Platform,
		Icon:        req.Icon,
		Domain:      req.Domain,
		Description: req.Description,
		Config:      config,
		FeeMode:     req.FeeMode,
		FeePercent:  req.FeePercent,
		FeeAmount:   req.FeeAmount,
		Enable:      req.Enable,
		Token:       random.KeyNew(8, 1),
	}
	err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
		if platformType == payment.Stripe {
			var cfg paymentModel.StripeConfig
			if err = cfg.Unmarshal([]byte(paymentMethod.Config)); err != nil {
				l.Errorf("[CreatePaymentMethod] unmarshal stripe config error: %s", err.Error())
				return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "unmarshal stripe config error: %s", err.Error())
			}
			if cfg.SecretKey == "" {
				l.Error("[CreatePaymentMethod] stripe secret key is empty")
				return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "stripe secret key is empty")
			}

			// Create Stripe webhook endpoint
			client := stripe.NewClient(stripe.Config{
				SecretKey: cfg.SecretKey,
				PublicKey: cfg.PublicKey,
			})
			url := fmt.Sprintf("%s/v1/notify/Stripe/%s", req.Domain, paymentMethod.Token)
			endpoint, err := client.CreateWebhookEndpoint(url)
			if err != nil {
				l.Errorw("[CreatePaymentMethod] create stripe webhook endpoint error", logger.Field("error", err.Error()))
				return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "create stripe webhook endpoint error: %s", err.Error())
			}
			cfg.WebhookSecret = endpoint.Secret
			content, _ := cfg.Marshal()
			paymentMethod.Config = string(content)
		}
		if err = tx.Model(&paymentModel.Payment{}).Create(paymentMethod).Error; err != nil {
			return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert payment method error: %s", err.Error())
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	resp = &types.PaymentConfig{}
	tool.DeepCopy(resp, paymentMethod)
	var configMap map[string]interface{}
	_ = json.Unmarshal([]byte(paymentMethod.Config), &configMap)
	resp.Config = configMap
	return
}

func parsePaymentPlatformConfig(ctx context.Context, platform payment.Platform, config interface{}) string {
	data, err := json.Marshal(config)
	if err != nil {
		logger.WithContext(ctx).Errorw("marshal config error", logger.Field("platform", platform), logger.Field("config", config), logger.Field("error", err.Error()))
		return ""
	}

	// 通用处理函数
	handleConfig := func(name string, target interface {
		Unmarshal([]byte) error
		Marshal() ([]byte, error)
	}) string {
		if err = target.Unmarshal(data); err != nil {
			logger.WithContext(ctx).Errorw("parse "+name+" config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
			return ""
		}
		content, err := target.Marshal()
		if err != nil {
			logger.WithContext(ctx).Errorw("marshal "+name+" config error", logger.Field("error", err.Error()))
			return ""
		}
		return string(content)
	}

	switch platform {
	case payment.Stripe:
		return handleConfig("Stripe", &paymentModel.StripeConfig{})
	case payment.AlipayF2F:
		return handleConfig("Alipay", &paymentModel.AlipayF2FConfig{})
	case payment.EPay:
		return handleConfig("Epay", &paymentModel.EPayConfig{})
	case payment.CryptoSaaS:
		return handleConfig("CryptoSaaS", &paymentModel.CryptoSaaSConfig{})
	case payment.AppleIAP:
		return handleConfig("AppleIAP", &paymentModel.AppleIAPConfig{})
	default:
		return ""
	}
}
