package payment

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"kra/internal/modules/system/biz"
	datapayment "kra/internal/modules/system/integration/payment"

	"gorm.io/gorm"
)

type paymentRepo struct{ data Provider }

func NewPaymentRepo(data Provider) biz.PaymentRepo { return &paymentRepo{data: data} }

func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
	for _, provider := range biz.SupportedPaymentProviders {
		var count int64
		if err := db.Model(&integrationConfigPO{}).Where("kind = ? AND provider = ?", integrationKindPayment, provider).Count(&count).Error; err != nil {
			return err
		}
		if count == 0 {
			if err := db.Create(&integrationConfigPO{Kind: integrationKindPayment, Provider: provider, Enabled: false, Config: "{}"}).Error; err != nil {
				return err
			}
		}
	}
	return nil
}

func (r *paymentRepo) row(ctx context.Context, provider string) (*integrationConfigPO, map[string]any, error) {
	var row integrationConfigPO
	if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil, biz.ErrPaymentProviderNotFound
		}
		return nil, nil, err
	}
	if !row.Enabled {
		return nil, nil, fmt.Errorf("支付渠道 %s 未启用", provider)
	}
	values := map[string]any{}
	if err := json.Unmarshal([]byte(row.Config), &values); err != nil {
		return nil, nil, fmt.Errorf("支付配置格式错误: %w", err)
	}
	return &row, values, nil
}

func (r *paymentRepo) ListConfigs(ctx context.Context) ([]*biz.PaymentConfig, error) {
	var rows []integrationConfigPO
	if err := r.data.DB().WithContext(ctx).Where("kind = ?", integrationKindPayment).Order("provider ASC").Find(&rows).Error; err != nil {
		return nil, err
	}
	out := make([]*biz.PaymentConfig, 0, len(rows))
	for _, row := range rows {
		values := map[string]any{}
		if json.Unmarshal([]byte(row.Config), &values) == nil {
			maskPaymentSecrets(values)
			masked, _ := json.Marshal(values)
			out = append(out, &biz.PaymentConfig{Provider: row.Provider, Enabled: row.Enabled, Values: masked})
			continue
		}
		out = append(out, &biz.PaymentConfig{Provider: row.Provider, Enabled: row.Enabled, Values: json.RawMessage("{}")})
	}
	return out, nil
}

func (r *paymentRepo) SaveConfig(ctx context.Context, config *biz.PaymentConfig) error {
	if config == nil || !contains(biz.SupportedPaymentProviders, config.Provider) {
		return errors.New("不支持的支付渠道")
	}
	if !json.Valid(config.Values) {
		return errors.New("支付配置必须是 JSON")
	}
	values := map[string]any{}
	if err := json.Unmarshal(config.Values, &values); err != nil {
		return errors.New("支付配置必须是 JSON 对象")
	}
	db := r.data.DB().WithContext(ctx)
	var row integrationConfigPO
	err := db.Where("kind = ? AND provider = ?", integrationKindPayment, config.Provider).First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		if config.Enabled {
			if err = validatePaymentConfig(config.Provider, values); err != nil {
				return err
			}
		}
		encoded, _ := json.Marshal(values)
		return db.Create(&integrationConfigPO{Kind: integrationKindPayment, Provider: config.Provider, Enabled: config.Enabled, Config: string(encoded)}).Error
	}
	if err != nil {
		return err
	}
	old := map[string]any{}
	_ = json.Unmarshal([]byte(row.Config), &old)
	mergePaymentSecrets(values, old)
	if config.Enabled {
		if err = validatePaymentConfig(config.Provider, values); err != nil {
			return err
		}
	}
	encoded, _ := json.Marshal(values)
	return db.Model(&row).Updates(map[string]any{"enabled": config.Enabled, "config": string(encoded)}).Error
}

func (r *paymentRepo) adapter(ctx context.Context, provider string) (datapayment.Adapter, map[string]any, error) {
	_, values, err := r.row(ctx, provider)
	if err != nil {
		return nil, nil, err
	}
	adapter, err := datapayment.New(provider)
	return adapter, values, err
}

func (r *paymentRepo) Create(ctx context.Context, req *biz.PaymentRequest) (*biz.PaymentResult, error) {
	if req == nil {
		return nil, errors.New("支付下单请求为空")
	}
	a, c, err := r.adapter(ctx, req.Provider)
	if err != nil {
		return nil, err
	}
	effective := *req
	effective.NotifyURL = strings.TrimSpace(text(c, "notify_url"))
	effective.ReturnURL = strings.TrimSpace(text(c, "return_url"))
	if paymentCreateRequiresNotifyURL(req.Provider, req.Extra, c) && effective.NotifyURL == "" {
		return nil, fmt.Errorf("支付渠道 %s 未配置服务端 notify_url", req.Provider)
	}
	return a.Create(ctx, &effective, c)
}

func paymentProviderRequiresNotifyURL(provider string) bool {
	switch provider {
	case biz.PaymentApple, biz.PaymentAllinPay, biz.PaymentSaobei, biz.PaymentPayPal:
		return false
	default:
		return true
	}
}

// paymentCreateRequiresNotifyURL keeps the repository-level URL guard aligned
// with the selected provider operation. Synchronous barcode/retail APIs return
// their execution result directly and do not consume notify_url; redirect and
// client-side prepay APIs still require the configured server callback URL.
func paymentCreateRequiresNotifyURL(provider string, extra, config map[string]any) bool {
	if !paymentProviderRequiresNotifyURL(provider) {
		return false
	}
	keys := []string{"method", "pay_method", "trade_type", "pay_type", "channel"}
	switch provider {
	case biz.PaymentAlipay, biz.PaymentAlipayV3:
		keys = []string{"method", "pay_method", "trade_type", "channel"}
	case biz.PaymentWechatV2:
		keys = []string{"trade_type", "pay_type", "method", "pay_method", "channel"}
	case biz.PaymentWechatV3:
		keys = []string{"trade_type", "pay_type", "method"}
	case biz.PaymentQQ:
		keys = []string{"trade_type", "pay_type", "method", "pay_method"}
	case biz.PaymentLakala:
		keys = []string{"method", "pay_method", "trade_type"}
	}
	value := firstAny(extra, keys...)
	if value == "" {
		value = firstAny(config, keys...)
	}
	normalized := strings.NewReplacer(".", "_", "-", "_", " ", "_").Replace(strings.ToLower(strings.TrimSpace(value)))
	switch provider {
	case biz.PaymentAlipay, biz.PaymentAlipayV3:
		return !contains([]string{"pay", "trade_pay", "alipay_trade_pay", "barcode", "barcode_pay", "micropay", "face_to_face"}, normalized)
	case biz.PaymentWechatV2:
		return !contains([]string{"micropay", "micro_pay", "barcode", "barcode_pay", "pay_code", "payment_code"}, normalized)
	case biz.PaymentWechatV3:
		return !contains([]string{"micropay", "micro_pay", "codepay", "code_pay", "barcode", "barcode_pay", "facepay", "face_pay"}, normalized)
	case biz.PaymentQQ:
		return !contains([]string{"micropay", "micro_pay", "barcode", "barcode_pay"}, normalized)
	case biz.PaymentLakala:
		return !contains([]string{"retail", "retail_pay", "micropay", "barcode"}, normalized)
	default:
		return true
	}
}
func (r *paymentRepo) Query(ctx context.Context, provider, tradeNo string) (*biz.PaymentResult, error) {
	a, c, err := r.adapter(ctx, provider)
	if err != nil {
		return nil, err
	}
	return a.Query(ctx, tradeNo, c)
}
func (r *paymentRepo) Refund(ctx context.Context, req *biz.PaymentRefundRequest) (*biz.PaymentResult, error) {
	if req == nil {
		return nil, errors.New("支付退款请求为空")
	}
	a, c, err := r.adapter(ctx, req.Provider)
	if err != nil {
		return nil, err
	}
	return a.Refund(ctx, req, c)
}
func (r *paymentRepo) HandleCallback(ctx context.Context, callback *biz.PaymentCallback) (*biz.PaymentResult, error) {
	if callback == nil {
		return nil, errors.New("支付回调为空")
	}
	a, c, err := r.adapter(ctx, callback.Provider)
	if err != nil {
		return nil, err
	}
	result, err := a.Callback(ctx, callback, c)
	if err != nil {
		return nil, &biz.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
	}
	if result == nil {
		err = errors.New("支付回调解析结果为空")
		return nil, &biz.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
	}
	result.SuccessAck = paymentCallbackAck(callback.Provider, c, true)
	result.FailureAck = paymentCallbackAck(callback.Provider, c, false)
	if result.Provider != callback.Provider {
		err = errors.New("支付回调渠道不匹配")
		return nil, &biz.PaymentCallbackError{Cause: err, Ack: result.FailureAck}
	}
	result.EventID = paymentCallbackEventID(callback, result)
	return result, nil
}

func paymentCallbackEventID(callback *biz.PaymentCallback, result *biz.PaymentResult) string {
	if result != nil {
		if eventID := strings.TrimSpace(result.EventID); eventID != "" {
			return eventID
		}
	}
	if callback == nil {
		return ""
	}
	fields := callbackFields(callback)
	if eventID := strings.TrimSpace(first(fields, "event_id", "notify_id", "notificationUUID", "id")); eventID != "" {
		return eventID
	}
	hash := sha256.Sum256(append([]byte(callback.Provider+"\x00"), callback.Body...))
	return hex.EncodeToString(hash[:])
}

func paymentCallbackAck(provider string, values map[string]any, success bool) biz.PaymentCallbackAck {
	ack := biz.DefaultPaymentCallbackAck(provider, success)
	prefix := "callback_success_"
	if !success {
		prefix = "callback_failure_"
	}
	if configured := strings.TrimSpace(text(values, prefix+"status")); configured != "" {
		if status, err := strconv.Atoi(configured); err == nil && status >= 200 && status <= 599 {
			ack.StatusCode = status
		}
	}
	if contentType := strings.TrimSpace(text(values, prefix+"content_type")); contentType != "" {
		ack.ContentType = contentType
	}
	if body, exists := values[prefix+"body"]; exists {
		ack.Body = []byte(fmt.Sprint(body))
	}
	return ack
}

func callbackFields(callback *biz.PaymentCallback) map[string]string {
	fields := map[string]string{}
	for key, value := range callback.Query {
		fields[key] = value
	}
	contentType := strings.ToLower(callback.Headers["Content-Type"])
	if strings.Contains(contentType, "application/x-www-form-urlencoded") {
		if values, err := url.ParseQuery(string(callback.Body)); err == nil {
			for key, value := range values {
				if len(value) > 0 {
					fields[key] = value[0]
				}
			}
		}
	}
	var object map[string]any
	if json.Unmarshal(callback.Body, &object) == nil {
		for key, value := range object {
			if text, ok := value.(string); ok {
				fields[key] = text
			}
		}
	}
	return fields
}

func first(values map[string]string, keys ...string) string {
	for _, key := range keys {
		if values[key] != "" {
			return values[key]
		}
	}
	return ""
}

var paymentSecretKeys = map[string]bool{
	"key": true, "secret": true, "secret_key": true, "private_key": true, "privatekey": true,
	"app_key": true, "client_key": true, "client_secret": true, "api_key": true,
	"api_v3_key": true, "api_v3key": true, "apiv3_key": true,
	"mch_key": true, "merchant_key": true, "signing_secret": true, "certificate": true,
	"cert": true, "root_cert": true, "platform_cert": true, "public_cert": true,
	"public_key": true, "client_cert": true, "key_content": true, "key_pem": true, "apiclient_key": true,
	"app_secret": true, "appsecret": true, "token": true, "access_token": true, "app_auth_token": true,
	"credential_code": true, "cus_id": true, "op_user_passwd": true, "p12": true, "pkcs12": true,
	"pkcs12_content": true, "merchant_private_key": true, "platform_key": true,
}

func maskPaymentSecrets(values map[string]any) {
	for key, value := range values {
		normalized := strings.ToLower(strings.ReplaceAll(key, "-", "_"))
		if paymentSecretKeys[normalized] || strings.Contains(normalized, "private") || strings.Contains(normalized, "secret") {
			if text, ok := value.(string); ok && text != "" {
				values[key] = "******"
			}
			continue
		}
		if nested, ok := value.(map[string]any); ok {
			maskPaymentSecrets(nested)
		}
	}
}
func mergePaymentSecrets(values, old map[string]any) {
	for key, value := range values {
		normalized := strings.ToLower(strings.ReplaceAll(key, "-", "_"))
		if paymentSecretKeys[normalized] || strings.Contains(normalized, "private") || strings.Contains(normalized, "secret") {
			if text, ok := value.(string); ok && text == "******" {
				if prior, exists := old[key]; exists {
					values[key] = prior
				}
			}
			continue
		}
		if nested, ok := value.(map[string]any); ok {
			if prior, ok := old[key].(map[string]any); ok {
				mergePaymentSecrets(nested, prior)
			}
		}
	}
}
func contains(values []string, value string) bool {
	for _, item := range values {
		if item == value {
			return true
		}
	}
	return false
}

func validatePaymentConfig(provider string, values map[string]any) error {
	required := []string{}
	configDriven := false
	switch provider {
	case biz.PaymentAlipay:
		required = []string{"app_id", "private_key", "public_key"}
	case biz.PaymentAlipayV3:
		required = []string{"app_id"}
		for _, group := range []struct {
			label string
			keys  []string
		}{
			{label: "private_key", keys: []string{"private_key", "private_key_content", "private_key_path"}},
			{label: "app_cert", keys: []string{"app_cert", "app_cert_content", "app_cert_path", "app_public_cert", "app_public_cert_content", "app_public_cert_path"}},
			{label: "root_cert", keys: []string{"root_cert", "root_cert_content", "root_cert_path", "alipay_root_cert", "alipay_root_cert_content", "alipay_root_cert_path"}},
			{label: "public_cert", keys: []string{"public_cert", "public_cert_content", "public_cert_path", "alipay_public_cert", "alipay_public_cert_content", "alipay_public_cert_path"}},
		} {
			if firstAny(values, group.keys...) == "" {
				return fmt.Errorf("%s 缺少配置字段 %s", provider, group.label)
			}
		}
	case biz.PaymentWechatV2:
		required = []string{"app_id", "merchant_id", "mch_key"}
		if firstAny(values, "client_cert", "cert_pem", "apiclient_cert") == "" || firstAny(values, "client_key", "key_pem", "apiclient_key") == "" {
			return fmt.Errorf("%s 退款要求同时配置 client_cert 和 client_key", provider)
		}
	case biz.PaymentWechatV3:
		required = []string{"app_id", "merchant_id", "serial_no", "private_key", "api_v3_key", "platform_cert"}
	case biz.PaymentApple:
		required = []string{"issuer_id", "key_id", "bundle_id", "private_key"}
		if configuredInt64(values, "price_divisor", 0) <= 0 {
			if _, ok := values["price_divisors"].(map[string]any); !ok {
				return fmt.Errorf("%s 缺少配置字段 price_divisor 或 price_divisors", provider)
			}
		}
	case biz.PaymentDouyin:
		required = []string{"app_id", "merchant_id", "serial_no", "api_key", "private_key", "platform_cert"}
	case biz.PaymentQQ:
		if firstAny(values, "mch_id", "merchant_id") == "" {
			return fmt.Errorf("%s 缺少配置字段 mch_id", provider)
		}
		required = []string{"api_key"}
		if !hasQQRefundCertificate(values) {
			return fmt.Errorf("%s 退款要求配置 cert_file + key_file、pkcs12_file 或对应的 *_content", provider)
		}
	case biz.PaymentAllinPay:
		required = []string{"cus_id", "app_id", "private_key", "public_key"}
	case biz.PaymentLakala:
		required = []string{"partner_code", "credential_code"}
	case biz.PaymentPayPal:
		required = []string{"client_id", "client_secret", "webhook_id"}
	case biz.PaymentSaobei:
		required = []string{"inst_no", "key", "merchant_no", "terminal_id", "access_token"}
	default:
		configDriven = true
		required = []string{
			"protocol_version", "app_id", "merchant_id", "create_url", "query_url", "refund_url", "query_status_field", "query_success_values",
			"query_trade_no_field", "query_provider_trade_no_field", "query_amount_field",
			"query_currency_field", "query_amount_scale", "callback_status_field",
			"callback_success_values", "callback_trade_no_field", "callback_provider_trade_no_field",
		}
		if firstAny(values, "app_key", "merchant_key", "signing_secret", "token") == "" {
			return fmt.Errorf("%s 缺少签名密钥", provider)
		}
		if err := validateConfiguredBreakdownFields(values); err != nil {
			return fmt.Errorf("%s 金额拆分配置无效: %w", provider, err)
		}
	}
	for _, key := range required {
		if configDriven && key == "query_amount_scale" {
			if _, exists := values[key]; !exists {
				return fmt.Errorf("%s 缺少配置字段 %s", provider, key)
			}
			continue
		}
		if strings.TrimSpace(text(values, key)) == "" {
			return fmt.Errorf("%s 缺少配置字段 %s", provider, key)
		}
	}
	if configDriven && !validPaymentAmountScale(configuredInt64(values, "query_amount_scale", 0)) {
		return fmt.Errorf("%s query_amount_scale 必须是正的 10 的幂", provider)
	}
	if provider == biz.PaymentDouyin && firstAny(values, "platform_serial_no", "platform_cert_serial") == "" {
		return fmt.Errorf("%s 缺少配置字段 platform_serial_no", provider)
	}
	if provider == biz.PaymentAllinPay {
		orderType := strings.ToLower(strings.TrimSpace(firstAny(values, "query_order_type", "order_type")))
		if orderType != "" && orderType != "reqsn" && orderType != "trxid" {
			return fmt.Errorf("%s query_order_type 必须是 reqsn 或 trxid", provider)
		}
	}
	if environment := strings.ToLower(strings.TrimSpace(text(values, "environment"))); environment != "" {
		switch provider {
		case biz.PaymentQQ, biz.PaymentDouyin, biz.PaymentLakala:
			if environment != "production" && environment != "prod" {
				return fmt.Errorf("%s 仅支持 production 环境", provider)
			}
		case biz.PaymentPayPal:
			if environment != "production" && environment != "prod" && environment != "sandbox" {
				return fmt.Errorf("%s environment 必须是 production 或 sandbox", provider)
			}
		default:
			if environment != "production" && environment != "prod" && environment != "sandbox" {
				return fmt.Errorf("%s environment 配置无效", provider)
			}
		}
	}
	if provider == biz.PaymentQQ {
		signType := strings.ToUpper(strings.TrimSpace(text(values, "sign_type")))
		if signType != "" && signType != "MD5" && signType != "HMAC-SHA256" {
			return fmt.Errorf("%s sign_type 必须是 MD5 或 HMAC-SHA256", provider)
		}
	}
	if err := validatePaymentCallbackAckConfig(values); err != nil {
		return fmt.Errorf("%s 回调响应配置无效: %w", provider, err)
	}
	return nil
}

func hasQQRefundCertificate(values map[string]any) bool {
	if firstAny(values, "pkcs12_file", "p12_file", "pkcs12_content") != "" {
		return true
	}
	if firstAny(values, "cert_file", "cert_path") != "" && firstAny(values, "key_file", "key_path") != "" {
		return true
	}
	return firstAny(values, "cert_content") != "" && firstAny(values, "key_content") != ""
}

func validPaymentAmountScale(scale int64) bool {
	if scale <= 0 {
		return false
	}
	for scale%10 == 0 {
		scale /= 10
	}
	return scale == 1
}

func validateConfiguredBreakdownFields(values map[string]any) error {
	for _, key := range []string{
		"query_payer_paid_amount_scale", "query_cash_paid_amount_scale", "query_point_paid_amount_scale",
		"query_discount_amount_scale", "query_provider_discount_amount_scale", "query_merchant_discount_amount_scale",
		"query_settlement_amount_scale",
	} {
		if _, exists := values[key]; exists && configuredInt64(values, key, 0) <= 0 {
			return fmt.Errorf("%s 必须是正整数", key)
		}
	}
	for _, key := range []string{
		"query_payer_paid_amount_field", "query_cash_paid_amount_field", "query_point_paid_amount_field",
		"query_discount_amount_field", "query_provider_discount_amount_field", "query_merchant_discount_amount_field",
		"query_settlement_amount_field", "query_payer_currency_field",
	} {
		if value, exists := values[key]; exists && strings.TrimSpace(fmt.Sprint(value)) == "" {
			return fmt.Errorf("%s 不能为空", key)
		}
	}
	return nil
}

func validatePaymentCallbackAckConfig(values map[string]any) error {
	for _, prefix := range []string{"callback_success_", "callback_failure_"} {
		if value := strings.TrimSpace(text(values, prefix+"status")); value != "" {
			status, err := strconv.Atoi(value)
			if err != nil || status < 200 || status > 599 {
				return fmt.Errorf("%sstatus 必须是 200-599", prefix)
			}
		}
		if contentType := text(values, prefix+"content_type"); strings.ContainsAny(contentType, "\r\n") || len(contentType) > 200 {
			return fmt.Errorf("%scontent_type 非法", prefix)
		}
		if body, exists := values[prefix+"body"]; exists && len(fmt.Sprint(body)) > 64<<10 {
			return fmt.Errorf("%sbody 超过 64KiB", prefix)
		}
	}
	return nil
}
