<?php


use app\extra\service\system\SystemService;
use app\extra\tools\CodeExtend;
use support\Response;
use Webman\Event\Event;


if (!function_exists('msubstr')) {
    /**
     * 字符串截取(同时去掉HTML与空白)
     * @param string $str
     * @param int $start
     * @param int $length
     * @param string $charset
     * @param bool $suffix
     * @return string
     */
    function msubstr(string $str, int $start = 0, int $length = 100,string $charset = "utf-8", bool $suffix = true): string
    {

        $str = preg_replace('/<[^>]+>/', '', preg_replace("/[\r\n\t ]{1,}/", ' ', delNt(strip_tags($str))));
        $str = preg_replace('/&(\w{4});/i', '', $str);

        // 直接返回
        if ($start == -1) {
            return $str;
        }

        if (function_exists("mb_substr")) {
            $slice = mb_substr($str, $start, $length, $charset);
        } elseif (function_exists('iconv_substr')) {
            $slice = iconv_substr($str, $start, $length, $charset);

        } else {
            $re['utf-8'] = "/[x01-x7f]|[xc2-xdf][x80-xbf]|[xe0-xef][x80-xbf]{2}|[xf0-xff][x80-xbf]{3}/";
            $re['gb2312'] = "/[x01-x7f]|[xb0-xf7][xa0-xfe]/";
            $re['gbk'] = "/[x01-x7f]|[x81-xfe][x40-xfe]/";
            $re['big5'] = "/[x01-x7f]|[x81-xfe]([x40-x7e]|xa1-xfe])/";
            preg_match_all($re[$charset], $str, $match);
            $slice = join("", array_slice($match[0], $start, $length));
        }

        $fix = '';
        if (strlen($slice) < strlen($str)) {
            $fix = '...';
        }
        return $suffix ? $slice . $fix : $slice;
    }
}

if (!function_exists('delNt')) {
    /**
     * 去掉连续空白
     * @param string $str 字符串
     * @return string
     */
    function delNt(string $str): string
    {
        $str = trim($str); //清除字符串两边的空格
        $str = strip_tags($str,""); //利用php自带的函数清除html格式
        $str = preg_replace("/\t/","",$str); //使用正则表达式替换内容，如：空格，换行，并将替换为空。
        $str = preg_replace("/\r\n/","",$str);
        $str = preg_replace("/\r/","",$str);
        $str = preg_replace("/\n/","",$str);
        $str = preg_replace("/ /","",$str);
        $str = preg_replace("/  /","",$str);  //匹配html中的空格
        return trim($str); //返回字符串
    }
}
if (!function_exists("between_time"))
{
    /**
     * 格式化起止时间(为了兼容前端RangePicker组件)
     * 2020-04-01T08:15:08.891Z => 1585670400
     * @param array $times
     * @param bool $isWithTime 是否包含时间
     * @return array
     */
    function between_time(array $times, bool $isWithTime = false): array
    {
        foreach ($times as &$time) {
            $time = trim($time, '&quot;');
            $time = str2date($time, $isWithTime);
        }
        return ['start_time' => current($times), 'end_time' => next($times)];
    }
}
if (!function_exists("str2date"))
{
    /**
     * 日期转换时间戳
     * 例如: 2020-04-01 08:15:08 => 1585670400
     * @param string $date
     * @param bool $isWithTime 是否包含时间
     * @return int
     */
    function str2date(string $date, bool $isWithTime = false): int
    {
        if (!$isWithTime) {
            $date = date('Y-m-d', strtotime($date));
        }
        return strtotime($date);
    }
}

if(!function_exists("hide_mobile")){

    /**
     * 手机号码脱敏
     * @param string $mobile
     * @param int $len 4 末尾4位 6 末尾2位
     * @return string
     */
    function hide_mobile(string $mobile,int $len = 6): string
    {
        return substr_replace($mobile, ($len==4 ? '****' : '******'), 3, $len);
    }
}

if (!function_exists('hide_str')) {
    /**
     * 将一个字符串部分字符用*替代隐藏
     * @param string $string 待转换的字符串
     * @param int $begin 起始位置，从0开始计数，当$type=4时，表示左侧保留长度
     * @param int $len 需要转换成*的字符个数，当$type=4时，表示右侧保留长度
     * @param int $type 转换类型：0，从左向右隐藏；1，从右向左隐藏；2，从指定字符位置分割前由右向左隐藏；3，从指定字符位置分割后由左向右隐藏；4，保留首末指定字符串中间用***代替
     * @param string $glue 分割符
     * @return string   处理后的字符串
     */
    function hide_str(string $string, int $begin = 3, int $len = 4, int $type = 0, string $glue = "@"): string
    {
        $array = array();
        if ($type == 0 || $type == 1 || $type == 4) {
            $strlen = $length = mb_strlen($string);
            while ($strlen) {
                $array[] = mb_substr($string, 0, 1, "utf8");
                $string = mb_substr($string, 1, $strlen, "utf8");
                $strlen = mb_strlen($string);
            }
        }
        if ($type == 0) {
            for ($i = $begin; $i < ($begin + $len); $i++) {
                if (isset($array[$i])) {
                    $array[$i] = "*";
                }
            }
            $string = implode("", $array);
        } elseif ($type == 1) {
            $array = array_reverse($array);
            for ($i = $begin; $i < ($begin + $len); $i++) {
                if (isset($array[$i])) {
                    $array[$i] = "*";
                }
            }
            $string = implode("", array_reverse($array));
        } elseif ($type == 2) {
            $array = explode($glue, $string);
            if (isset($array[0])) {
                $array[0] = hide_str($array[0], $begin, $len, 1);
            }
            $string = implode($glue, $array);
        } elseif ($type == 3) {
            $array = explode($glue, $string);
            if (isset($array[1])) {
                $array[1] = hide_str($array[1], $begin, $len, 0);
            }
            $string = implode($glue, $array);
        } elseif ($type == 4) {
            $left = $begin;
            $right = $len;
            $tem = array();
            for ($i = 0; $i < ($length - $right); $i++) {
                if (isset($array[$i])) {
                    $tem[] = $i >= $left ? "" : $array[$i];
                }
            }
            $tem[] = '*****';
            $array = array_chunk(array_reverse($array), $right);
            $array = array_reverse($array[0]);
            for ($i = 0; $i < $right; $i++) {
                if (isset($array[$i])) {
                    $tem[] = $array[$i];
                }
            }
            $string = implode("", $tem);
        }
        return $string;
    }
}

if (!function_exists('list_sort_by')) {
    /**
     *----------------------------------------------------------
     * 对查询结果集进行排序
     *----------------------------------------------------------
     * @access public
     *----------------------------------------------------------
     * @param array $list 查询结果
     * @param string $field 排序的字段名
     * @param string $sortBy 排序类型
     * @switch string  asc正向排序 desc逆向排序 nat自然排序
     *----------------------------------------------------------
     * @return array
     *----------------------------------------------------------
     */
    function list_sort_by(array $list, string $field, string $sortBy = 'asc'): array
    {
        if (!empty($list)) {
            $refer = $resultSet = array();
            foreach ($list as $i => $data)
                $refer[$i] = &$data[$field];
            switch ($sortBy) {
                case 'asc': // 正向排序
                    asort($refer);
                    break;
                case 'desc':// 逆向排序
                    arsort($refer);
                    break;
                case 'nat': // 自然排序
                    natcasesort($refer);
                    break;
            }
            foreach ($refer as $key => $val)
                $resultSet[] = &$list[$key];
            return $resultSet;
        }
        return [];
    }
}


if (!function_exists('supplement_id')) {
    /**
     * 用户ID风格
     * @param string $id
     * @return string
     */
    function supplement_id(string $id): string
    {
        $len = strlen($id);
        $buf = '000000';
        return $len < 6 ? substr($buf, 0, (6 - $len)) . $id : $id;
    }
}
if (!function_exists('createOrderId')) {
    /**
     * 生成订单号
     * @param string $letter
     * @param int $length
     * @return string
     */
    function createOrderId(string $letter = '', int $length = 3): string
    {
        $gradual = 0;
        $orderId = date('YmdHis') . mt_rand(10000000, 99999999);
        $lengths = strlen($orderId);

        // 循环处理随机数
        for ($i = 0; $i < $lengths; $i++) {
            $gradual += (int)(substr($orderId, $i, 1));
        }

        if (empty($letter)) {
            $letter = get_order_letter($length);
        }

        $code = (100 - $gradual % 100) % 100;
        return $letter . $orderId . str_pad((string)$code, 2, '0', STR_PAD_LEFT);
    }
}

if (!function_exists('get_order_letter')) {
    /**
     * 生成订单短ID
     * @param int $length
     * @return string
     */
    function get_order_letter(int $length = 2): string
    {
        $letter_all = range('A', 'Z');
        shuffle($letter_all);
        $letter_array = array_diff($letter_all, ['I', 'O']);
        $letter = array_rand(array_flip($letter_array), $length);
        return implode('', $letter);
    }
}

/**
 * 过滤emoji表情
 * @param string $str
 * @return string
 */
if (!function_exists('removeEmoji')) {
    function removeEmoji(string $str = '') : string
    {
        $str = preg_replace('/[\x{1F600}-\x{1F64F}]/u', '', $str);
        $str = preg_replace('/[\x{1F300}-\x{1F5FF}]/u', '', $str);
        $str = preg_replace('/[\x{1F680}-\x{1F6FF}]/u', '', $str);
        $str = preg_replace('/[\x{2600}-\x{26FF}]/u', '', $str);
        return preg_replace('/[\x{2700}-\x{27BF}]/u', '', $str);
    }
}

if (!function_exists('object_array')) {
    /**
     * @param $array
     * @return array
     */
    function object_array($array): array
    {
        if(is_object($array)) {
            $array = (array)$array;
        }
        if(is_array($array)) {
            foreach($array as $key=>$value) {
                $array[$key] = object_array($value);
            }
        }
        return $array;
    }
}

if (!function_exists("getDateFull"))
{
    /**
     * @param string $format
     * @param string $time
     * @return string
     */
    function getDateFull(string $format = "Y-m-d H:i:s",string $time = ""): string
    {
        if (empty($time)) $time = time();
        return date($format??'Y-m-d H:i:s',$time);
    }
}

if (!function_exists("events"))
{
    /**
     * 事件发布
     * @param $name
     * @param $data
     * @return array|mixed|null
     */
    function events($name,$data): mixed
    {
        return Event::dispatch($name,$data);
    }
}

if (!function_exists("success")) {
    /**
     * @param string $msg
     * @param array $data
     * @param int $code
     * @return Response
     */
    function success(string $msg = "", array $data = [], int $code = 1): Response
    {
        return json(compact('code', 'data', 'msg'));
    }
}

if (!function_exists("successTrans")) {
    /**
     * 消息返回
     * @param string $message
     * @param array $data
     * @param int $code
     * @return Response
     */
    function successTrans(string $message, array $data = [], int $code = 1): Response
    {
        $msg = trans($message);
        return json(compact("msg", "code", "data"));
    }
}

if (!function_exists("error")) {
    /**
     * @param $msg
     * @param array $data
     * @param int $code
     * @return Response
     */
    function error($msg, array $data = [], int $code = 0): Response
    {
        return json(compact('code', 'data', 'msg'));
    }
}

if (!function_exists("errorTrans")) {
    /**
     * 消息返回
     * @param string $message
     * @param array $data
     * @param int $code
     * @return Response
     */
    function errorTrans(string $message = "", array $data = [], int $code = 0): Response
    {
        $msg = trans($message);
        return json(compact("msg", "code", "data"));
    }
}

if (!function_exists("pageFormat")) {
    /**
     * @param $data
     * @param int $size
     * @return array {page:"",pageSize:"",rows:[],total:""}
     */
    function pageFormat($data, int $size = 10): array
    {
        if (empty($data)) return [];
        return [
            'total'     => $data->total(),
            'page'      => $data->currentPage(),
            'pageSize'  => $size,
            'rows'      => $data->items()
        ];
    }
}


if(!function_exists('format_money')){
    function format_money($str,$len = '2',$append = ""): string
    {
        if (empty($str)) {
            return "0.00";
        }
        return number_format($str, $len, ".", $append);
    }
}


if (!function_exists('sConf')) {

    /**
     * 获取或配置系统参数
     * @param string $name 参数名称
     * @param string|null $value 参数内容
     */
    function sConf(string $name = '', string $value = null)
    {
        if (is_null($value) && is_string($name)) {
            return SystemService::get($name);
        } else {
            return SystemService::set($name, $value);
        }
    }
}

if (!function_exists('sData')) {

    /**
     * JSON 数据读取与存储
     * @param string $name 数据名称
     * @param mixed $value 数据内容
     */
    function sData(string $name,mixed $value = null)
    {
        if (is_null($value)) {
            return SystemService::getData($name);
        } else {
            return SystemService::setData($name, $value);
        }
    }
}


if (!function_exists('sOplog')) {
    /**
     * 写入系统日志
     * @param string $action 日志行为
     * @param string $content 日志内容
     * @param string $userName
     * @return boolean
     */
    function sOplog(string $action, string $content, string $userName): bool
    {
        return SystemService::setOplog($action, $content,$userName);
    }
}



if(!function_exists('store_region')){
    /**
     * 返回附近司机数据
     * @param $latitude
     * @param $longitude
     * @param $type 1 返回查询影响记录数；2 返回数据数组
     */
    function store_region($latitude,$longitude,$type = 1,$where = 'status = 1',$field="*"){
        $sql = "select ".$field." from(
SELECT id,poi_id,poi_name,status,poi_address,longitude,latitude,
ROUND(6378.138*2*ASIN(SQRT(POW(SIN(({$latitude}*PI()/180-latitude*PI()/180)/2),2)+COS({$latitude}*PI()/180)*COS(latitude*PI()/180)*POW(SIN(({$longitude}*PI()/180-longitude*PI()/180)/2),2)))*1000) AS juli
FROM saas_store_shop where ".$where.") as tmp_table_name order by juli asc";
        if($type == 1){
            return \think\facade\Db::execute($sql);
        }
        return \think\facade\Db::query($sql);
    }
}

if(!function_exists('getHourlyTimeSlots')){
    function getHourlyTimeSlots($startHour = 9, $endHour = 22, $format = 'H:i'): array
    {
        $slots = [];

        for ($hour = $startHour; $hour < $endHour; $hour++) {
            $startTime = sprintf('%02d:00', $hour);
            $endTime = sprintf('%02d:00', $hour + 1);

            $slots[] = [
                'start' => $startTime,
                'end' => $endTime,
                'display' => $startTime . ' - ' . $endTime
            ];
        }

        return $slots;
    }
}

if (!function_exists("strToUniqueNumberV4"))
{
    /**
     * 名称加密
     * @param string $str
     * @param string $salt
     * @return string
     */
    function strToUniqueNumberV4(string $str = "", string $salt = ''): string
    {
        // 添加盐值增加唯一性
        $str = $str . $salt;
        // 使用多种哈希组合
        $crc = abs(crc32($str));
        $md5 = hexdec(substr(md5($str), 0, 8));
        $sha1 = hexdec(substr(sha1($str), 0, 8));
        // 组合并取模
        $number = ($crc + $md5 + $sha1) % 1000000000000;
        // 使用时间戳微调确保唯一性（针对同一字符串）
        static $lastStr = '';
        static $counter = 0;
        if ($str === $lastStr) {
            $number = ($number + $counter) % 1000000000000;
        } else {
            $lastStr = $str;
            $counter = 0;
        }
        return str_pad($number, 12, '0', STR_PAD_LEFT);
    }
}


if (!function_exists('enbase64url')) {
    /**
     * Base64安全URL编码
     * @param string $string
     * @return string
     */
    function enbase64url(string $string): string
    {
        return CodeExtend::enSafe64($string);
    }
}
if (!function_exists('debase64url')) {
    /**
     * Base64安全URL解码
     * @param string $string
     * @return string
     */
    function debase64url(string $string): string
    {
        return CodeExtend::deSafe64($string);
    }
}
if (!function_exists('unique_arr')) {

    /**
     * 多维数组根据指定字段去重
     * @param array $array 原始数组
     * @param string $field 去重字段名
     * @return array 去重后的数组
     */
    function unique_arr(array $array, string $field): array
    {
        $tmp = [];
        foreach ($array as $key => $val) {
            // 用去重字段的值作为临时键，重复会自动覆盖
            $tmp[$val[$field]] = $val;
        }
        // 重置索引返回
        return array_values($tmp);
    }
}

if (!function_exists("format_money_bit"))
{
    /**
     * 金额格式化：过万显示 万、百万、千万、亿
     * @param float $money    原始金额
     * @param int $decimal  保留几位小数
     * @return string
     */
    function format_money_bit(float $money, int $decimal = 2): string
    {
        $money = (float)$money;

        if ($money >= 100000000) {
            // 亿
            $result = number_format($money / 100000000, $decimal) . '亿';
        } elseif ($money >= 10000000) {
            // 千万
            $result = number_format($money / 10000000, $decimal) . '千万';
        } elseif ($money >= 1000000) {
            // 百万
            $result = number_format($money / 1000000, $decimal) . '百万';
        } elseif ($money >= 10000) {
            // 万
            $result = number_format($money / 10000, $decimal) . '万';
        } else {
            // 小于1万，直接显示
            $result = number_format($money, $decimal);
        }

        $result = rtrim($result, '0');
        $result = rtrim($result, '.');

        return $result;
    }
}
if (!function_exists('formatMoneyKw')) {

    /**
     * 金额格式化 千k 万w
     * @param float|int $num 原始金额
     * @param int $decimal
     * @return string
     */
    function formatMoneyKw(float|int $num,int $decimal = 2): string
    {
        // 先转浮点
        $num = (float)$num;

        if ($num >= 10000) {
            // 万
            $val = $num / 10000;
            $unit = 'w';
        } elseif ($num >= 1000) {
            // 千
            $val = $num / 1000;
            $unit = 'k';
        } else {
            // 小于1000直接返回字符串
            return (string)$num;
        }
        // 保留2位小数，去除末尾多余0和小数点
        $val = rtrim(rtrim(sprintf("%.{$decimal}f", $val), '0'), '.');
        return $val . $unit;
    }
}

if (!function_exists("strip_img_attributes"))
{
    /**
     * 纯正则过滤 img 标签的指定属性
     * @param string $html 富文本
     * @param array $attrs 要删除的属性：width, height, style, border 等
     * @return string
     */
    function strip_img_attributes($html, $attrs = ['width', 'height', 'style', 'border']) {
        // 拼接正则：匹配 属性="xxx" 或 属性='xxx' 或 属性=xxx
        $pattern = '/(' . implode('|', $attrs) . ')\s*=\s*("[^"]*"|\'[^\']*\'|\S*)\s*/i';

        // 一次性替换掉所有 img 里的目标属性
        return preg_replace($pattern, '', $html);
    }
}


if(! function_exists('emojiToUnicode'))
{
    /**
     * @param string $str
     * @return string
     */
    function emojiToUnicode(string $str): string
    {
        return trim(json_encode($str, JSON_UNESCAPED_SLASHES), '"');
    }
}

if(! function_exists('unicodeToEmoji'))
{
    function unicodeToEmoji(string $str): string
    {
        return json_decode("\"{$str}\"");
    }
}

if(! function_exists('removeEmoji2'))
{
    /**
     * 清除字符串中所有emoji表情
     * @param string $str
     * @return string
     */
    function removeEmoji2(string $str): string
    {
        // 1. 匹配所有emoji Unicode区间
        $emoji = '/[\x{1F600}-\x{1F64F}\x{1F300}-\x{1F5FF}\x{1F680}-\x{1F6FF}\x{1F1E0}-\x{1F1FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}\x{FE00}-\x{FE0F}\x{1F900}-\x{1F9FF}\x{1F018}-\x{1F0FF}\x{2300}-\x{23FF}]/u';
        $str = preg_replace($emoji, '', $str);

        // 2. 过滤颜文字、日文符号、装饰特殊符号、全角特殊符号
        $face_sym = '/[\x{3000}-\x{303F}\x{3040}-\x{30FF}\x{31F0}-\x{31FF}\x{2460}-\x{24FF}\x{2100}-\x{214F}\x{2190}-\x{21FF}\x{2500}-\x{257F}\x{25A0}-\x{25FF}\x{2900}-\x{297F}\x{2B00}-\x{2BFF}\x{4DC0}-\x{4DFF}\x{1F000}-\x{1F02F}\x{00A0}-\x{00AF}\x{02B0}-\x{02FF}]/u';
        $str = preg_replace($face_sym, '', $str);

        // 3. 只允许保留：中文、大小写字母、数字、逗号句号顿号冒号分号问号感叹号空格
        // 不想保留空格就删掉 \s
        $allow = '/[^\x{4E00}-\x{9FA5}a-zA-Z0-9\s\,\.、\:;\?\!]/u';
        $str = preg_replace($allow, '', $str);

        // 去除多余连续空格
        $str = preg_replace('/\s+/', ' ', $str);
        return trim($str);
    }
}