<?php
// +----------------------------------------------------------------------
// | onezan-admin 快速开发的管理平台
// +----------------------------------------------------------------------
// | 官方网址：https://www.onezan.cn
// +----------------------------------------------------------------------
// | onezan团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: 543885307@qq.com
// +----------------------------------------------------------------------
namespace app\service;


/**
 * 输入数据归一化助手
 * @package app\service
 */
class InputHelper
{
     /**
     * 归一化时间值
     * @param mixed $val
     * @return int
     */
    public static function normalizeTimeValue($val): int
    {
        if ($val === null) return 0;
        if (is_int($val)) return $val > 0 ? $val : 0;
        if (is_float($val)) return (int)$val;
        $raw = trim((string)$val);
        if ($raw === '') return 0;
        if (preg_match('/^\d+$/', $raw) === 1) {
            $ts = (int)$raw;
            return $ts > 0 ? $ts : 0;
        }
        $ts = (int)(strtotime($raw) ?: 0);
        return $ts > 0 ? $ts : 0;
    }

     /**
     * 归一化金额值
     * @param mixed $val
     * @return float
     */
    public static function formatAmount($val, int $decimalsMin = 0, int $decimalsMax = 4): string
    {
        $num = is_numeric($val) ? (float)$val : 0.0;
        if ($decimalsMax < 0) $decimalsMax = 0;
        if ($decimalsMin < 0) $decimalsMin = 0;
        if ($decimalsMin > $decimalsMax) $decimalsMin = $decimalsMax;
        $rounded = round($num, $decimalsMax);
        $s = number_format($rounded, $decimalsMax, '.', '');
        if ($decimalsMax > 0) {
            $s = rtrim(rtrim($s, '0'), '.');
            if ($decimalsMin > 0) {
                if (strpos($s, '.') === false) {
                    $s .= '.' . str_repeat('0', $decimalsMin);
                } else {
                    $parts = explode('.', $s, 2);
                    $cur = strlen($parts[1]);
                    if ($cur < $decimalsMin) {
                        $s .= str_repeat('0', $decimalsMin - $cur);
                    }
                }
            }
        } else {
            if ($decimalsMin > 0) {
                $s .= '.' . str_repeat('0', $decimalsMin);
            }
        }
        return $s;
    }

     /**
     * 归一化汇率值
     * @param mixed $val
     * @return float
     */
    public static function formatRate($val, int $decimalsMax = 4): string
    {
        return self::formatAmount($val, 0, $decimalsMax) . '%';
    }

     /**
     * 归一化金额值（不四舍五入）
     * @param mixed $val
     * @return float
     */
    public static function formatAmountNoRound($val, int $decimalsMin = 0, int $decimalsMax = 4): string
    {
        if ($decimalsMax < 0) $decimalsMax = 0;
        if ($decimalsMin < 0) $decimalsMin = 0;
        if ($decimalsMin > $decimalsMax) $decimalsMin = $decimalsMax;

        $s = self::normalizeDecimalString($val, $decimalsMax);
        if ($decimalsMax > 0) {
            $s = rtrim(rtrim($s, '0'), '.');
            if ($decimalsMin > 0) {
                if (strpos($s, '.') === false) {
                    $s .= '.' . str_repeat('0', $decimalsMin);
                } else {
                    $parts = explode('.', $s, 2);
                    $cur = strlen((string)($parts[1] ?? ''));
                    if ($cur < $decimalsMin) $s .= str_repeat('0', $decimalsMin - $cur);
                }
            }
        } else {
            if ($decimalsMin > 0) $s .= '.' . str_repeat('0', $decimalsMin);
        }
        return $s;
    }

     /**
     * 归一化小数字符串
     * @param mixed $value
     * @return string
     */
    public static function normalizeDecimalString($value, int $scale = 4): string
    {
        $s = trim((string)($value ?? '0'));
        if ($s === '') $s = '0';
        $neg = false;
        if (strlen($s) > 0 && $s[0] === '-') {
            $neg = true;
            $s = substr($s, 1);
        }
        $s = trim($s);
        if ($s === '') $s = '0';
        if (!preg_match('/^\d+(\.\d+)?$/', $s)) {
            $s = '0';
        }
        $parts = explode('.', $s, 2);
        $int = $parts[0] ?? '0';
        $frac = $parts[1] ?? '';
        $int = ltrim($int, '0');
        if ($int === '') $int = '0';
        $frac = preg_replace('/\D/', '', (string)$frac);
        if ($scale < 0) $scale = 0;
        if ($scale === 0) {
            if ($int === '0') $neg = false;
            return ($neg ? '-' : '') . $int;
        }
        $frac = substr($frac, 0, $scale);
        $frac = str_pad($frac, $scale, '0');
        if ($int === '0' && preg_match('/^0+$/', $frac) === 1) $neg = false;
        return ($neg ? '-' : '') . $int . '.' . $frac;
    }

     /**
     * 取绝对值
     * @param mixed $value
     * @return string
     */
    public static function absDecimalString($value): string
    {
        $s = trim((string)($value ?? '0'));
        if ($s !== '' && $s[0] === '-') $s = substr($s, 1);
        return $s;
    }

     /**
     * 比较小数字符串
     * @param mixed $a
     * @param mixed $b
     * @param int $scale
     * @return int
     */
    public static function decimalCmp($a, $b, int $scale = 4): int
    {
        if (function_exists('bccomp')) {
            return bccomp((string)$a, (string)$b, $scale);
        }
        [$aneg, $au] = self::decimalToUnits($a, $scale);
        [$bneg, $bu] = self::decimalToUnits($b, $scale);
        if ($aneg !== $bneg) return $aneg ? -1 : 1;
        $cmp = self::cmpUnits($au, $bu);
        return $aneg ? -$cmp : $cmp;
    }

     /**
     * 加法
     * @param mixed $a
     * @param mixed $b
     * @param int $scale
     * @return string
     */
    public static function decimalAdd($a, $b, int $scale = 4): string
    {
        if (function_exists('bcadd')) {
            return self::normalizeDecimalString(bcadd((string)$a, (string)$b, $scale), $scale);
        }
        [$aneg, $au] = self::decimalToUnits($a, $scale);
        [$bneg, $bu] = self::decimalToUnits($b, $scale);
        if ($aneg === $bneg) {
            $sum = self::addUnits($au, $bu);
            return self::unitsToDecimal($sum, $scale, $aneg);
        }
        $cmp = self::cmpUnits($au, $bu);
        if ($cmp === 0) return self::unitsToDecimal('0', $scale, false);
        if ($cmp > 0) {
            $diff = self::subUnits($au, $bu);
            return self::unitsToDecimal($diff, $scale, $aneg);
        }
        $diff = self::subUnits($bu, $au);
        return self::unitsToDecimal($diff, $scale, $bneg);
    }

     /**
     * 减法
     * @param mixed $a
     * @param mixed $b
     * @param int $scale
     * @return string
     */
    private static function decimalToUnits($value, int $scale): array
    {
        $norm = self::normalizeDecimalString($value, $scale);
        $neg = false;
        if ($norm !== '' && $norm[0] === '-') {
            $neg = true;
            $norm = substr($norm, 1);
        }
        $parts = explode('.', $norm, 2);
        $units = ($parts[0] ?? '0') . ($parts[1] ?? '');
        $units = ltrim($units, '0');
        if ($units === '') $units = '0';
        if ($units === '0') $neg = false;
        return [$neg, $units];
    }

     /**
     * 小数字符串转换为单位字符串
     * @param string $units
     * @param int $scale
     * @param bool $neg
     * @return string
     */
    private static function unitsToDecimal(string $units, int $scale, bool $neg): string
    {
        $units = ltrim($units, '0');
        if ($units === '') $units = '0';
        if ($units === '0') $neg = false;
        if ($scale <= 0) return ($neg ? '-' : '') . $units;
        if (strlen($units) <= $scale) {
            $units = str_pad($units, $scale + 1, '0', STR_PAD_LEFT);
        }
        $int = substr($units, 0, -$scale);
        $frac = substr($units, -$scale);
        return ($neg ? '-' : '') . $int . '.' . $frac;
    }

     /**
     * 比较单位字符串
     * @param string $a
     * @param string $b
     * @return int
     */
    private static function cmpUnits(string $a, string $b): int
    {
        $a = ltrim($a, '0');
        $b = ltrim($b, '0');
        if ($a === '') $a = '0';
        if ($b === '') $b = '0';
        if (strlen($a) !== strlen($b)) return strlen($a) < strlen($b) ? -1 : 1;
        if ($a === $b) return 0;
        return $a < $b ? -1 : 1;
    }

     /**
     * 加法
     * @param string $a
     * @param string $b
     * @return string
     */
    private static function addUnits(string $a, string $b): string
    {
        $i = strlen($a) - 1;
        $j = strlen($b) - 1;
        $carry = 0;
        $out = '';
        while ($i >= 0 || $j >= 0 || $carry) {
            $da = $i >= 0 ? (int)$a[$i] : 0;
            $db = $j >= 0 ? (int)$b[$j] : 0;
            $sum = $da + $db + $carry;
            $out = (string)($sum % 10) . $out;
            $carry = intdiv($sum, 10);
            $i--;
            $j--;
        }
        $out = ltrim($out, '0');
        return $out === '' ? '0' : $out;
    }

     /**
     * 减法
     * @param string $a
     * @param string $b
     * @return string
     */
    private static function subUnits(string $a, string $b): string
    {
        $i = strlen($a) - 1;
        $j = strlen($b) - 1;
        $borrow = 0;
        $out = '';
        while ($i >= 0) {
            $da = (int)$a[$i] - $borrow;
            $db = $j >= 0 ? (int)$b[$j] : 0;
            if ($da < $db) {
                $da += 10;
                $borrow = 1;
            } else {
                $borrow = 0;
            }
            $out = (string)($da - $db) . $out;
            $i--;
            $j--;
        }
        $out = ltrim($out, '0');
        return $out === '' ? '0' : $out;
    }
}
