package payment

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"strings"

	"kra/internal/biz"

	gopayApple "github.com/go-pay/gopay/apple"
	"github.com/google/uuid"
)

type appleAdapter struct{}

func (a *appleAdapter) Create(_ context.Context, req *biz.PaymentRequest, _ map[string]any) (*biz.PaymentResult, error) {
	if req == nil {
		return nil, errors.New("Apple 内购下单参数为空")
	}
	productID := ""
	if req.Extra != nil {
		productID, _ = req.Extra["product_id"].(string)
	}
	if productID == "" {
		return nil, errors.New("Apple 内购缺少 extra.product_id")
	}
	if _, err := uuid.Parse(req.TradeNo); err != nil {
		return nil, errors.New("Apple 内购 tradeNo 必须是用于 appAccountToken 的 UUID")
	}
	payload, _ := json.Marshal(map[string]any{"product_id": productID, "app_account_token": req.TradeNo, "client_only": true, "trade_no": req.TradeNo})
	return &biz.PaymentResult{Provider: biz.PaymentApple, Status: "client_pending", TradeNo: req.TradeNo, Payload: payload}, nil
}
func (a *appleAdapter) Query(ctx context.Context, transactionID string, c map[string]any) (*biz.PaymentResult, error) {
	transactionID = strings.TrimSpace(transactionID)
	if transactionID == "" {
		return nil, errors.New("Apple 查询缺少 transactionId")
	}
	client, err := gopayApple.NewClient(
		text(c, "issuer_id"),
		text(c, "bundle_id"),
		text(c, "key_id"),
		text(c, "private_key"),
		!strings.EqualFold(text(c, "environment"), "sandbox"),
	)
	if err != nil {
		return nil, err
	}
	response, err := client.GetTransactionInfo(ctx, transactionID)
	if err != nil {
		return nil, err
	}
	if response == nil || response.SignedTransactionInfo == "" {
		return nil, errors.New("Apple 查询响应缺少 signedTransactionInfo")
	}
	if err = validateAppleJWSChain(response.SignedTransactionInfo); err != nil {
		return nil, err
	}
	transaction, err := response.DecodeSignedTransaction()
	if err != nil {
		return nil, err
	}
	claims, err := appleClaims(transaction)
	if err != nil {
		return nil, err
	}
	if bundleID := text(c, "bundle_id"); bundleID != "" && fmt.Sprint(claims["bundleId"]) != bundleID {
		return nil, errors.New("Apple 交易 bundleId 不匹配")
	}
	if err = validateAppleEnvironment(claims, c); err != nil {
		return nil, err
	}
	payload, _ := json.Marshal(claims)
	result, err := applePaymentResult(transactionID, claims, payload, c)
	if err != nil {
		return nil, err
	}
	if result.ProviderTradeNo == "" {
		return nil, errors.New("Apple 交易缺少 transactionId")
	}
	return result, nil
}
func (a *appleAdapter) Refund(context.Context, *biz.PaymentRefundRequest, map[string]any) (*biz.PaymentResult, error) {
	return nil, errors.New("Apple 内购退款由 App Store 管理，服务端不提供主动退款接口")
}
func (a *appleAdapter) Callback(_ context.Context, callback *biz.PaymentCallback, c map[string]any) (*biz.PaymentResult, error) {
	if callback == nil {
		return nil, errors.New("Apple 回调为空")
	}
	var body struct {
		SignedPayload string `json:"signedPayload"`
	}
	if err := json.Unmarshal(callback.Body, &body); err != nil {
		return nil, err
	}
	if body.SignedPayload == "" {
		return nil, errors.New("Apple 回调缺少 signedPayload")
	}
	if err := validateAppleJWSChain(body.SignedPayload); err != nil {
		return nil, err
	}
	notification, err := gopayApple.DecodeSignedPayload(body.SignedPayload)
	if err != nil {
		return nil, err
	}
	if notificationID := strings.TrimSpace(notification.NotificationUUID); notificationID != "" {
		if callback.Query == nil {
			callback.Query = map[string]string{}
		}
		callback.Query["event_id"] = notificationID
	}
	if notification.Data == nil {
		return nil, errors.New("Apple 回调缺少 data")
	}
	if err = validateAppleJWSChain(notification.Data.SignedTransactionInfo); err != nil {
		return nil, err
	}
	transaction, err := notification.DecodeTransactionInfo()
	if err != nil {
		return nil, err
	}
	claims, err := appleClaims(transaction)
	if err != nil {
		return nil, err
	}
	if bundleID := text(c, "bundle_id"); bundleID != "" && fmt.Sprint(claims["bundleId"]) != bundleID {
		return nil, errors.New("Apple 回调 bundleId 不匹配")
	}
	if err = validateAppleEnvironment(claims, c); err != nil {
		return nil, err
	}
	payload, _ := json.Marshal(claims)
	tradeNo := strings.TrimSpace(fmt.Sprint(claims["appAccountToken"]))
	transactionID := strings.TrimSpace(fmt.Sprint(claims["transactionId"]))
	if tradeNo == "" || tradeNo == "<nil>" || transactionID == "" || transactionID == "<nil>" {
		return nil, errors.New("Apple 回调缺少 appAccountToken 或 transactionId")
	}
	if _, parseErr := uuid.Parse(tradeNo); parseErr != nil {
		return nil, errors.New("Apple 回调 appAccountToken 不是有效 UUID")
	}
	return &biz.PaymentResult{Provider: biz.PaymentApple, Status: "pending", TradeNo: tradeNo, ProviderTradeNo: transactionID, QueryID: transactionID, Payload: payload}, nil
}

func appleClaims(value any) (map[string]any, error) {
	payload, err := json.Marshal(value)
	if err != nil {
		return nil, err
	}
	claims := map[string]any{}
	if err := json.Unmarshal(payload, &claims); err != nil {
		return nil, err
	}
	return claims, nil
}

func applePaymentResult(transactionID string, claims map[string]any, payload []byte, c map[string]any) (*biz.PaymentResult, error) {
	revoked, err := appleRevoked(claims)
	if err != nil {
		return nil, err
	}
	status := "success"
	if revoked {
		status = "failed"
	}
	providerTradeNo := strings.TrimSpace(fmt.Sprint(claims["transactionId"]))
	if providerTradeNo == "" || providerTradeNo == "<nil>" || providerTradeNo != transactionID {
		return nil, errors.New("Apple 查询返回的 transactionId 不匹配")
	}
	tradeNo := strings.TrimSpace(fmt.Sprint(claims["appAccountToken"]))
	if tradeNo == "" || tradeNo == "<nil>" {
		return nil, errors.New("Apple 交易缺少 appAccountToken")
	}
	if _, err := uuid.Parse(tradeNo); err != nil {
		return nil, errors.New("Apple 交易 appAccountToken 不是有效 UUID")
	}
	result := &biz.PaymentResult{Provider: biz.PaymentApple, Status: status, TradeNo: tradeNo, ProviderTradeNo: providerTradeNo, QueryID: providerTradeNo, Payload: payload}
	if status != "success" {
		return result, nil
	}
	result.Currency = strings.ToUpper(strings.TrimSpace(fmt.Sprint(claims["currency"])))
	if result.Currency == "<nil>" {
		result.Currency = ""
	}
	if result.Currency == "" {
		return nil, errors.New("Apple 交易缺少 currency")
	}
	price, err := parseIntegerAmount(strings.TrimSpace(fmt.Sprint(claims["price"])))
	if err != nil {
		return nil, fmt.Errorf("解析 Apple 交易 price: %w", err)
	}
	divisor := applePriceDivisor(c, result.Currency)
	if divisor <= 0 || price%divisor != 0 {
		return nil, errors.New("Apple price_divisor 未配置或交易金额无法精确换算到最小货币单位")
	}
	result.Amount = price / divisor
	result.PayerPaidAmount = result.Amount
	result.CashPaidAmount = result.Amount
	result.SettlementAmount = result.Amount
	result.PayerCurrency = result.Currency
	result.AmountBreakdownKnown = true
	return result, nil
}

func appleRevoked(claims map[string]any) (bool, error) {
	value, exists := claims["revocationDate"]
	if !exists || value == nil {
		return false, nil
	}
	switch number := value.(type) {
	case float64:
		if math.IsNaN(number) || math.IsInf(number, 0) || number < 0 || math.Trunc(number) != number {
			return false, errors.New("Apple revocationDate 无效")
		}
		return number > 0, nil
	case json.Number:
		parsed, err := number.Int64()
		if err != nil || parsed < 0 {
			return false, errors.New("Apple revocationDate 无效")
		}
		return parsed > 0, nil
	case int64:
		if number < 0 {
			return false, errors.New("Apple revocationDate 无效")
		}
		return number > 0, nil
	case int:
		if number < 0 {
			return false, errors.New("Apple revocationDate 无效")
		}
		return number > 0, nil
	}
	textValue := strings.TrimSpace(fmt.Sprint(value))
	if textValue == "" || textValue == "<nil>" || textValue == "0" {
		return false, nil
	}
	parsed, err := parseIntegerAmount(textValue)
	if err != nil {
		return false, fmt.Errorf("解析 Apple revocationDate: %w", err)
	}
	return parsed > 0, nil
}

func applePriceDivisor(c map[string]any, currency string) int64 {
	if values, ok := c["price_divisors"].(map[string]any); ok {
		for key, value := range values {
			if strings.EqualFold(strings.TrimSpace(key), currency) {
				return configuredInt64(map[string]any{"value": value}, "value", 0)
			}
		}
	}
	return configuredInt64(c, "price_divisor", 0)
}

func validateAppleEnvironment(claims map[string]any, c map[string]any) error {
	actual := strings.ToLower(strings.TrimSpace(fmt.Sprint(claims["environment"])))
	if actual == "" || actual == "<nil>" {
		return errors.New("Apple 交易缺少 environment")
	}
	expected := "production"
	if strings.EqualFold(text(c, "environment"), "sandbox") {
		expected = "sandbox"
	}
	if actual != expected {
		return errors.New("Apple 交易 environment 不匹配")
	}
	return nil
}
