<?php

namespace App\libs\helpers;

use app\Auth;
use App\libs\cache\redis\Redis;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;

class Helper
{
    protected $client;
    protected $key;

    public function __construct()
    {
        $this->key = 'odsniK8Xk0tMuJbwyztEPjRLLo';
        $this->sk  = '9936be1a0451712e0f58866ac6be7a4f';
    }

    public function opensd($post = [])
    {
        $API_URL     = 'https://api.wike.cc/api/painting/opensd';
        $post['key'] = $this->key;
        $res         = $this->send($API_URL, $post, 'POST', true, $this->sk);

        return $res;
    }

    public function opensdDetail($id)
    {
        $API_URL     = 'https://api.wike.cc/api/painting/result';
        $post['key'] = $this->key;
        $post['id']  = $id;
        $res         = $this->send($API_URL, $post, 'GET', true, $this->sk);

        return $res;
    }
    /**
     * 获取框架 root 路径
     *
     * @return bool
     */
    public static function frameRootPath()
    {
        return DIRECTORY_SEPARATOR === '\\' ? 'E:\TP6_V4' : '/Users/wangdakun/Desktop/wangdakun/TP6_V4';
    }
    public static function send($API_URL, $get_post_data, $type, $ifsign, $sk)
    {
        $get_post_data = http_build_query($get_post_data);
        if ($ifsign) {
            $sign = md5($get_post_data . $sk);
            $res  = self::send_curl($API_URL, $type, $get_post_data, $sign);
        } else {
            $res = self::send_curl($API_URL, $type, $get_post_data, null);
        }

        return $res;
    }

    public static function send_curl($API_URL, $type, $get_post_data, $sign)
    {
        $ch = curl_init();
        if ('POST' == $type) {
            curl_setopt($ch, CURLOPT_URL, $API_URL);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $get_post_data);
        } elseif ('GET' == $type) {
            curl_setopt($ch, CURLOPT_URL, $API_URL . '?' . $get_post_data);
        }
        if ($sign) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['sign:' . $sign]);
        }
        curl_setopt($ch, CURLOPT_REFERER, $API_URL);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        $resdata = curl_exec($ch);
        curl_close($ch);

        return $resdata;
    }

    public static function getAccessToken()
    {
        $curl     = curl_init();
        $postData = [
            'grant_type'    => getenv('BAIDU_GRANT_TYPE'),
            'client_id'     => getenv('BAIDU_CLIENT_ID'),
            'client_secret' => getenv('BAIDU_CLIENT_SECRET'),
        ];
        curl_setopt_array($curl, [
            CURLOPT_URL            => 'https://aip.baidubce.com/oauth/2.0/token',
            CURLOPT_CUSTOMREQUEST  => 'POST',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POSTFIELDS     => http_build_query($postData),
        ]);
        $response = curl_exec($curl);
        curl_close($curl);
        $rtn = json_decode($response);

        return $rtn->access_token;
    }

    /**
     * @desc    过滤emoji表情
     *
     * @return string|string[]|null
     */
    public static function filterEmoji($str)
    {
        return preg_replace_callback('/./u', static fn (array $match) => \strlen($match[0]) >= 4 ? '' : $match[0], $str);
    }

    /**
     * @desc    字节转换
     *
     * @return string
     */
    public static function sizeConvert(int $numSize)
    {
        if ($numSize >= 1073741824) {
            $charSize = round($numSize / 1024 ** 3 * 100) / 100 . ' GB';
        } elseif ($numSize >= 1048576) {
            // 转成MB
            $charSize = round($numSize / 1024 ** 2 * 100) / 100 . ' MB';
        } elseif ($numSize >= 1024) {
            // 转成KB
            $charSize = round($numSize / 1024 * 100) / 100 . ' KB';
        } else {
            // 不转换直接输出
            $charSize = $numSize . ' B';
        }

        return $charSize;
    }

    /**
     * @desc    数字单位转换
     *
     * @return string
     */
    public static function numberConvert(int $number)
    {
        if ($number >= 1000 && $number < 10000) {
            $number = sprintf('%.1f', $number / 1000) . ' K';
        } elseif ($number >= 10000) {
            $number = sprintf('%.1f', $number / 10000) . ' W';
        } elseif ($number >= 1000000) {
            $number = sprintf('%.1f', $number / 10000) . ' M';
        }

        return $number;
    }

    /**
     * @desc    生成 hash token
     */
    public static function hashToken(string $value)
    {
        $hash = hash_hmac('sha1', $value . mt_rand() . time(), mt_rand(), true);

        return str_replace('=', '', strtr(base64_encode($hash), '+/', '-_'));
    }

    /**
     * @desc    是否微信环境
     *
     * @return bool|mixed
     */
    public static function isWeixin()
    {
        return str_contains($_SERVER['HTTP_USER_AGENT'], 'MicroMessenger') ? true : false;
    }

    /**
     * @desc    ssl 加密
     *
     * @param        $str 待加密字符
     * @param string $key 加密key
     * @param string $iv  偏移iv
     *
     * @return string
     */
    public static function encrypt($str, $key = 'cdlchdwxcdh5', $iv = 'cdlchd0123456789')
    {
        return bin2hex(openssl_encrypt($str, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv));
    }

    /**
     * @desc    ssl 解密
     *
     * @param string $str 待解密字符
     * @param string $key 加密key
     * @param string $iv  偏移iv
     *
     * @return string
     */
    public static function decrypt($str, $key = 'cdlchdwxcdh5', $iv = 'cdlchd0123456789')
    {
        return openssl_decrypt(hex2bin($str), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    }

    /**
     * @desc    记录日志
     */
    public static function apiLogs(array $data, string $remarks = ''): void
    {
        $request = request();
        $data    = [
            'token'      => $request->header('token', null),
            'uid'        => Auth::$userId,
            'url'        => $request->url(),
            'controller' => $request->controller(),
            'func'       => $request->action(),
            'method'     => $request->method(),
            'ip'         => $request->ip(),
            'params'     => json_encode($data, 256),
            'remarks'    => $remarks,
            'day'        => strtotime('today'),
            'create'     => time(),
        ];
        $rdsConf = Config::get('lc.redis');
        if ($rdsConf['log_open']) {
            $redis = Redis::instance();
            $redis->select($rdsConf['db']);
            $redis->rPush(env('id') . '_apilogs', json_encode($data, 256));
        } else {
            Db::name('api_logs')->insert($data);
        }
    }

    /**
     * @desc 	将xml转为array
     *
     * @return array
     *
     * @throws \Exception
     */
    public static function xmlToArray($xml)
    {
        if (!$xml) {
            throw new \Exception('xml数据异常！');
        }
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);

        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * @desc    输出xml字符
     *
     * @param array $data
     *
     * @return string
     *
     * @throws \Exception
     */
    public static function arrayToXml($data)
    {
        if (!\is_array($data) || \count($data) <= 0) {
            throw new \Exception('数组数据异常！');
        }

        $xml = '<xml>';
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<' . $key . '>' . $val . '</' . $key . '>';
            } else {
                $xml .= '<' . $key . '><![CDATA[' . $val . ']]></' . $key . '>';
            }
        }
        $xml .= '</xml>';

        return $xml;
    }

    /**
     * @desc	按字典序生成签名
     *
     * @param array  $data 签名数据
     * @param string $key  前面key
     *
     * @return string
     */
    public static function makeSign(array $data, string $key)
    {
        // 1. 按字典序排序参数
        ksort($data);
        $string = self::signUrlParams($data);
        // 2. 在string后加入KEY
        $string = $string . '&key=' . $key;
        // 3. MD5加密
        $string = md5($string);

        // 4. 所有字符转为大写
        return strtoupper($string);
    }

    /**
     * @desc 获取毫秒级别的时间戳
     */
    public static function millisecond()
    {
        // 获取毫秒的时间戳
        $time  = explode(' ', microtime());
        $time  = $time[1] . ($time[0] * 1000);
        $time2 = explode('.', $time);

        return $time2[0];
    }

    /**
     * @desc	产生随机字符串，不长于32位
     *
     * @param int $length
     *
     * @return string 产生的随机字符串
     */
    public static function generateNonceStr($length = 32)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str   = '';
        for ($i = 0; $i < $length; ++$i) {
            $str .= substr($chars, mt_rand(0, \strlen($chars) - 1), 1);
        }

        return $str;
    }

    /**
     * @desc    对emoji表情转义
     *
     * @return string
     */
    public static function emojiEncode($str)
    {
        $strEncode = '';

        $length = mb_strlen($str, 'utf-8');

        for ($i = 0; $i < $length; ++$i) {
            $tmpStr = mb_substr($str, $i, 1, 'utf-8');
            if (\strlen($tmpStr) >= 4) {
                $strEncode .= '[[EMOJI:' . rawurlencode($tmpStr) . ']]';
            } else {
                $strEncode .= $tmpStr;
            }
        }

        return $strEncode;
    }

    /**
     * @desc    对emoji表情转反义
     *
     * @return string|string[]|null
     */
    public static function emojiDecode($str)
    {
        return preg_replace_callback('|\[\[EMOJI:(.*?)\]\]|', static fn ($matches) => rawurldecode($matches[1]), $str);
    }

    /**
     * @desc    判断请求机型
     *
     * @return string
     */
    public static function deviceType()
    {
        $agent = strtolower($_SERVER['HTTP_USER_AGENT']);
        $type  = 'other';
        // 分别进行判断
        if (strpos($agent, 'iphone') || strpos($agent, 'ipad')) {
            $type = 'ios';
        }
        if (strpos($agent, 'android')) {
            $type = 'android';
        }

        return $type;
    }

    /**
     * @desc    记录日志
     *
     * @param string    $filename 文件名
     * @param string    $log      日志内容
     * @param float|int $fileSize 文件日志大小，默认最大2M，超过则重命名
     */
    public static function log(string $filename, string $log, $fileSize = 2 * 1024 * 1024): void
    {
        $path = \dirname($filename);
        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }

        if (is_file($filename) && floor($fileSize) <= filesize($filename)) {
            try {
                rename($filename, \dirname($filename) . \DIRECTORY_SEPARATOR . time() . '-' . basename($filename));
            } catch (\Exception $e) {
            }
        }

        error_log($log, 3, $filename);
    }

    /**
     * 求两点之间的距离.
     *
     * @param     $thisLongitude 当前经度
     * @param     $thisLatitude  当前纬度
     * @param     $stayLongitude 待计算经度
     * @param     $stayLatitude  待计算纬度
     * @param int $unit          单位，1米，2千米
     * @param int $decimals      保留小数点几位
     *
     * @return string
     */
    public static function calculateDistance($thisLongitude, $thisLatitude, $stayLongitude, $stayLatitude, $unit = 1, $decimals = 0)
    {
        // 原始坐标纬度弧度 deg2rad()函数将角度转换为弧度
        $oriLatRad = deg2rad($thisLatitude);
        // 新坐标纬度弧度
        $newLatRad = deg2rad($stayLatitude);
        // 坐标纬度弧度差
        $diffLatRad = deg2rad($thisLatitude) - deg2rad($stayLatitude);

        // 经度弧度差
        $diffLngRad = deg2rad($thisLongitude) - deg2rad($stayLongitude);

        // sin 求正弦值 参数单位为弧度 sin(2) => 0.90929742682568
        // cos 求余弦值 参数单位为弧度 cos(0.2) => 0.98006657784124
        // sqrt 求平方根 sqrt(2) => 1.4142135623731
        // asin 求反正弦 参数单位为弧度 asin(2) => 1.4142135623731
        // pow 求幂 pow(2, 2) => 4
        $distance = 2 * asin(sqrt(sin($diffLatRad / 2) ** 2 + cos($oriLatRad) * cos($newLatRad) * sin($diffLngRad / 2) ** 2)) * 6378.137 * 1000;
        if (2 === $unit) {
            $distance = $distance / 1000;
        }

        return number_format($distance, $decimals);
    }

    /**
     * 导出csv数据.
     *
     * @param      $title   表名
     * @param      $cell    表头
     * @param      $data    数据
     * @param null $closure
     */
    public static function exportCsv($title, $cell, $data, $closure = null): void
    {
        set_time_limit(0);
        ini_set('memory_limit', '1024M');
        $output = fopen('php://output', 'w') || exit("can't open php://output");
        if (ob_get_contents()) {
            ob_clean();
        }
        header('Content-Type: application/force-download');
        header('Content-type: text/csv;charset=utf-8');
        header('Content-Disposition: attachment; filename=' . $title . '.csv');
        echo \chr(0xEF) . \chr(0xBB) . \chr(0xBF);
        fputcsv($output, $cell);
        foreach ($data as $v) {
            if ($closure instanceof \Closure) {
                $v = $closure($v);
            }
            fputcsv($output, array_values($v));
        }
        fclose($output) || exit("can't close php://output");

        exit;
    }

    /**
     * @desc	签名格式化成url参数
     *
     * @param array $params 格式化参数
     *
     * @return string
     */
    private static function signUrlParams(array $params)
    {
        $buff = '';
        foreach ($params as $k => $v) {
            if ('sign' !== $k && '' !== $v && !\is_array($v)) {
                $buff .= $k . '=' . $v . '&';
            }
        }

        return trim($buff, '&');
    }
}
