package payment

import (
	"encoding/json"
	"errors"
	"fmt"
	bizpayment "kra/internal/biz/payment"
	"net/url"
	"strings"

	"kra/pkg/paymentkit"
)

func callbackFields(callback *bizpayment.PaymentCallback) map[string]string {
	fields := map[string]string{}
	if callback == nil {
		return fields
	}
	for key, value := range callback.Query {
		fields[key] = value
	}
	contentType := ""
	for key, value := range callback.Headers {
		if strings.EqualFold(key, "Content-Type") {
			contentType = strings.ToLower(value)
			break
		}
	}
	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 stringValue, ok := value.(string); ok {
				fields[key] = stringValue
			}
		}
	}
	return fields
}

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

func text(values map[string]any, key string) string {
	value, _ := values[key].(string)
	return value
}

func firstAny(values map[string]any, keys ...string) string {
	for _, key := range keys {
		if value, ok := values[key].(string); ok && value != "" {
			return value
		}
	}
	return ""
}

// Compatibility shims keep channel adapters focused on protocol and
// persistence concerns while stateless parsing rules live in paymentutil.
func normalizePaymentStatus(value, fallback string) string {
	return paymentutil.NormalizeStatus(value, fallback)
}

func parseIntegerAmount(value string) (int64, error) {
	return paymentutil.ParseIntegerAmount(value)
}

func parseDecimalAmount(value string, scale int64) (int64, error) {
	return paymentutil.ParseDecimalAmount(value, scale)
}

func formatDecimalAmount(amount, scale int64) (string, error) {
	return paymentutil.FormatDecimalAmount(amount, scale)
}

func configuredInt64(values map[string]any, key string, fallback int64) int64 {
	return paymentutil.ConfiguredInt64(values, key, fallback)
}

func jsonObject(raw []byte) map[string]any {
	return paymentutil.JSONObject(raw)
}

func nestedString(value any, keys ...string) string {
	return paymentutil.NestedString(value, keys...)
}

func valueAtPath(value any, path string) any {
	return paymentutil.ValueAtPath(value, path)
}

func stringAtPath(value any, path string) string {
	return paymentutil.StringAtPath(value, path)
}

func configuredValues(values map[string]any, key string) []string {
	return paymentutil.ConfiguredValues(values, key)
}

func containsFold(values []string, value string) bool {
	return paymentutil.ContainsFold(values, value)
}

// parseConfiguredAmount reads a provider amount field using an explicit unit
// conversion. Missing fields are reported separately so callers can avoid
// manufacturing a complete breakdown from incomplete provider responses.
func parseConfiguredAmount(object any, config map[string]any, fieldKey, scaleKey string, fallbackScale int64) (int64, bool, error) {
	path := strings.TrimSpace(text(config, fieldKey))
	if path == "" {
		return 0, false, nil
	}
	raw := valueAtPath(object, path)
	if raw == nil || strings.TrimSpace(fmt.Sprint(raw)) == "" || strings.EqualFold(strings.TrimSpace(fmt.Sprint(raw)), "<nil>") {
		return 0, false, nil
	}
	value := strings.TrimSpace(fmt.Sprint(raw))
	scale := configuredInt64(config, scaleKey, fallbackScale)
	if scale <= 0 {
		return 0, false, errors.New("支付金额换算比例无效")
	}
	amount, err := parseIntegerAmount(value)
	if scale != 1 {
		amount, err = parseDecimalAmount(value, scale)
	}
	if err != nil {
		return 0, true, err
	}
	if amount < 0 {
		return 0, true, errors.New("支付金额不能为负数")
	}
	return amount, true, nil
}

func amountFromDecimalField(values map[string]any, key string, fallback int64) (int64, bool, error) {
	value := strings.TrimSpace(fmt.Sprint(values[key]))
	if value == "" || value == "<nil>" {
		return 0, false, nil
	}
	amount, err := parseDecimalAmount(value, fallback)
	return amount, true, err
}
