package alipay

import (
	"context"
	"net/url"

	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/tool"
	"github.com/pkg/errors"
	"github.com/smartwalle/alipay/v3"
)

type Config struct {
	AppId       string
	PrivateKey  string
	PublicKey   string
	InvoiceName string
	NotifyURL   string
	Sandbox     bool
}

type Notification struct {
	OrderNo string
	Amount  int64
	Status  Status
}

type Status string

const (
	Success  Status = "TRADE_SUCCESS"
	Pending  Status = "WAIT_BUYER_PAY"
	Closed   Status = "TRADE_CLOSED"
	Finished Status = "TRADE_FINISHED"
	Error    Status = "TRADE_ERROR"
)

type Client struct {
	Config
	client *alipay.Client
}
type Order struct {
	OrderNo string
	Amount  int64
}

func NewClient(c Config) *Client {
	client, err := alipay.New(c.AppId, c.PrivateKey, c.Sandbox)
	if err != nil {
		logger.Error("[Alipay] NewClient failed: ", logger.Field("errors", err), logger.Field("config", c))
		return nil
	}
	err = client.LoadAliPayPublicKey(c.PublicKey)
	if err != nil {
		logger.Error("[Alipay] NewClient failed: ", logger.Field("errors", err), logger.Field("config", c))
	}
	return &Client{
		Config: c,
		client: client,
	}
}

func (c *Client) PreCreateTrade(ctx context.Context, order Order) (string, error) {
	amountString := tool.FormatFloat(float64(order.Amount)/float64(100), 2)
	trade, err := c.client.TradePreCreate(ctx, alipay.TradePreCreate{
		Trade: alipay.Trade{
			OutTradeNo:  order.OrderNo,
			TotalAmount: amountString,
			Subject:     c.InvoiceName,
			NotifyURL:   c.NotifyURL,
		},
	})
	if err != nil {
		return "", err
	}
	if trade.Code != alipay.CodeSuccess {
		return "", errors.New("PreCreateTrade failed: " + trade.Msg)
	}
	return trade.QRCode, nil
}

func (c *Client) QueryTrade(ctx context.Context, orderNo string) (Status, error) {
	trade, err := c.client.TradeQuery(ctx, alipay.TradeQuery{
		OutTradeNo: orderNo,
	})
	if err != nil {
		return Error, err
	}
	switch trade.TradeStatus {
	case alipay.TradeStatusSuccess:
		return Success, nil
	case alipay.TradeStatusWaitBuyerPay:
		return Pending, nil
	case alipay.TradeStatusClosed:
		return Closed, nil
	case alipay.TradeStatusFinished:
		return Finished, nil
	default:
		return Error, errors.New("QueryTrade failed: " + trade.Msg)
	}
}

func (c *Client) DecodeNotification(form url.Values) (*Notification, error) {
	notify, err := c.client.DecodeNotification(form)
	if err != nil {
		return nil, err
	}

	return &Notification{
		OrderNo: notify.OutTradeNo,
		Amount:  int64(tool.FormatStringToFloat(notify.TotalAmount) * 100),
		Status:  Status(notify.TradeStatus),
	}, nil
}
