package paymentutil

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

// ParseIntegerAmount parses an integer amount expressed in the smallest
// currency unit. Floating point is intentionally not involved.
func ParseIntegerAmount(value string) (int64, error) {
	value = strings.TrimSpace(value)
	if value == "" {
		return 0, errors.New("支付金额为空")
	}
	return strconv.ParseInt(value, 10, 64)
}

// ParseDecimalAmount converts a major-unit decimal string to an integer amount
// using a power-of-ten scale, without using floating point.
func ParseDecimalAmount(value string, scale int64) (int64, error) {
	value = strings.TrimSpace(value)
	if value == "" || scale <= 0 {
		return 0, errors.New("支付金额或换算比例无效")
	}
	if strings.HasPrefix(value, "-") {
		return 0, errors.New("支付金额不能为负数")
	}
	parts := strings.Split(value, ".")
	if len(parts) > 2 || parts[0] == "" {
		return 0, fmt.Errorf("支付金额格式错误: %s", value)
	}
	digits, err := scaleDigits(scale)
	if err != nil {
		return 0, err
	}
	whole, err := strconv.ParseInt(parts[0], 10, 64)
	if err != nil {
		return 0, err
	}
	fraction := ""
	if len(parts) == 2 {
		fraction = parts[1]
	}
	if len(fraction) > digits {
		return 0, fmt.Errorf("支付金额精度超过 %d 位", digits)
	}
	fraction += strings.Repeat("0", digits-len(fraction))
	fractionValue := int64(0)
	if fraction != "" {
		fractionValue, err = strconv.ParseInt(fraction, 10, 64)
		if err != nil {
			return 0, err
		}
	}
	if whole > (int64(^uint64(0)>>1)-fractionValue)/scale {
		return 0, errors.New("支付金额超出范围")
	}
	return whole*scale + fractionValue, nil
}

func FormatDecimalAmount(amount, scale int64) (string, error) {
	if amount <= 0 || scale <= 0 {
		return "", errors.New("支付金额或换算比例无效")
	}
	digits, err := scaleDigits(scale)
	if err != nil {
		return "", err
	}
	if digits == 0 {
		return strconv.FormatInt(amount, 10), nil
	}
	return fmt.Sprintf("%d.%0*d", amount/scale, digits, amount%scale), nil
}

func scaleDigits(scale int64) (int, error) {
	digits := 0
	for n := scale; n > 1 && n%10 == 0; n /= 10 {
		digits++
	}
	if scale <= 0 || scale != pow10(digits) {
		return 0, errors.New("支付金额换算比例必须是 10 的幂")
	}
	return digits, nil
}

func pow10(digits int) int64 {
	value := int64(1)
	for i := 0; i < digits; i++ {
		if value > (int64(^uint64(0)>>1))/10 {
			return 0
		}
		value *= 10
	}
	return value
}
