package stripe

import (
	"encoding/json"
	"fmt"
	"strconv"

	"github.com/stripe/stripe-go/v81/webhookendpoint"

	"github.com/perfect-panel/server/pkg/logger"
	"github.com/stripe/stripe-go/v81"
	"github.com/stripe/stripe-go/v81/customer"
	"github.com/stripe/stripe-go/v81/ephemeralkey"
	"github.com/stripe/stripe-go/v81/paymentintent"
	"github.com/stripe/stripe-go/v81/paymentmethod"
	"github.com/stripe/stripe-go/v81/webhook"
)

const APIVersion = "2024-04-10"

type Config struct {
	PublicKey     string
	SecretKey     string
	WebhookSecret string
}

type User struct {
	UserId int64
	Email  string
}
type NotifyResult struct {
	EventType string
	OrderNo   string
	TradeNo   string
	Method    string
	UserId    int64
	Amount    int64
}
type Order struct {
	OrderNo   string
	Subscribe string
	Amount    int64
	Currency  string
	Payment   string
}

type Client struct {
	Config
}

type PaymentSheet struct {
	ClientSecret   string
	EphemeralKey   string
	Customer       string
	PublishableKey string
	TradeNo        string
}

func NewClient(config Config) *Client {
	return &Client{
		Config: config,
	}
}

func (c *Client) CreatePaymentSheet(order *Order, user *User) (*PaymentSheet, error) {
	stripe.Key = c.SecretKey
	// Create a new Stripe customer if it does not exist
	customerDataRes, err := c.SearchStripeCustomer(user)
	if err != nil {
		return nil, err
	}
	if customerDataRes == nil {
		customerDataRes, err = c.CreateCustomer(user)
		if err != nil {
			return nil, err
		}
	}
	// Create Ephemeral Key
	ekParams := &stripe.EphemeralKeyParams{
		Customer:      stripe.String(customerDataRes.ID),
		StripeVersion: stripe.String(APIVersion),
	}
	ek, err := ephemeralkey.New(ekParams)
	if err != nil {
		return nil, err
	}
	// Create Payment Intent
	params := &stripe.PaymentIntentParams{
		Amount:   stripe.Int64(order.Amount),
		Customer: stripe.String(customerDataRes.ID),
		Currency: stripe.String(order.Currency),
		PaymentMethodTypes: []*string{
			stripe.String(order.Payment),
		},
		Metadata: map[string]string{
			"order_no":  order.OrderNo,
			"user_id":   strconv.FormatInt(user.UserId, 10),
			"subscribe": order.Subscribe,
		},
	}
	result, err := paymentintent.New(params)
	if err != nil {
		return nil, err
	}
	return &PaymentSheet{
		ClientSecret:   result.ClientSecret,
		EphemeralKey:   ek.Secret,
		Customer:       customerDataRes.ID,
		PublishableKey: c.PublicKey,
		TradeNo:        result.ID,
	}, nil
}

// SearchStripeCustomer  Search for a Stripe customer by email or user ID
func (c *Client) SearchStripeCustomer(user *User) (*stripe.Customer, error) {
	stripe.Key = c.SecretKey
	params := &stripe.CustomerSearchParams{}
	if user.Email != "" {
		params.SearchParams.Query = fmt.Sprintf("email:'%s'", user.Email)
	} else {
		params.SearchParams.Query = fmt.Sprintf("metadata['user_id']:'%d'", user.UserId)
	}
	result := customer.Search(params)
	if result.Err() != nil {
		fmt.Printf("Error: %v\n", result.Err().Error())
		return nil, result.Err()
	}

	if len(result.CustomerSearchResult().Data) != 0 {
		return result.CustomerSearchResult().Data[0], nil
	}
	return nil, nil
}

// CreateCustomer Create a new Stripe customer
func (c *Client) CreateCustomer(user *User) (*stripe.Customer, error) {
	stripe.Key = c.SecretKey
	customerData := &stripe.CustomerParams{}
	if user.Email != "" {
		customerData.Email = &user.Email
	}
	customerData.AddMetadata("user_id", strconv.FormatInt(user.UserId, 10))
	return customer.New(customerData)
}

// QueryOrderStatus Query the status of the order
func (c *Client) QueryOrderStatus(orderNo string) (bool, error) {
	stripe.Key = c.SecretKey
	intent, err := paymentintent.Get(orderNo, nil)
	if err != nil {
		return false, err
	}
	return intent.Status == "succeeded", err
}

// ParseNotify
func (c *Client) ParseNotify(payload []byte, signature string) (*NotifyResult, error) {
	event, err := webhook.ConstructEvent(payload, signature, c.Config.WebhookSecret)
	if err != nil {
		return nil, err
	}
	var paymentIntent stripe.PaymentIntent
	err = json.Unmarshal(event.Data.Raw, &paymentIntent)
	if err != nil {
		logger.Error("Failed to unmarshal payment intent", logger.Field("error", err.Error()))
		return nil, err
	}
	orderNo := paymentIntent.Metadata["order_no"]
	userId := paymentIntent.Metadata["user_id"]
	var method string
	if paymentIntent.PaymentMethod != nil && paymentIntent.PaymentMethod.ID != "" {
		fmt.Println("paymentMethod:", paymentIntent.PaymentMethod.ID)
		result, err := c.RetrievePaymentMethod(paymentIntent.PaymentMethod.ID)
		if err != nil {
			logger.Error("[stripe] Payment callback query payment method error", logger.Field("errors", err.Error()))
		}
		if result != nil {
			method = string(result.Type)
		}
	}
	// userId string 转 int64
	uid, _ := strconv.ParseInt(userId, 10, 64)
	return &NotifyResult{
		EventType: string(event.Type),
		OrderNo:   orderNo,
		TradeNo:   paymentIntent.ID,
		UserId:    uid,
		Method:    method,
		Amount:    paymentIntent.Amount,
	}, nil
}

// RetrievePaymentMethod 查询支付方式
func (c *Client) RetrievePaymentMethod(id string) (*stripe.PaymentMethod, error) {
	stripe.Key = c.SecretKey
	return paymentmethod.Get(id, nil)
}

// CreateWebhookEndpoint 创建 webhook endpoint
func (c *Client) CreateWebhookEndpoint(url string) (*stripe.WebhookEndpoint, error) {
	stripe.Key = c.SecretKey
	params := &stripe.WebhookEndpointParams{
		URL: stripe.String(url),
		EnabledEvents: []*string{
			stripe.String("payment_intent.succeeded"),
			stripe.String("payment_intent.payment_failed"),
		},
	}
	return webhookendpoint.New(params)
}
