<?php
/**
 * Created by PhpStorm.
 * User: wangchen
 * Date: 2019-05-07
 * Time: 15:22
 */

namespace App\Libs;


use App\Cache\CacheKeys;
use Hashids;
use App\Exceptions\ApiException;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

class Utils
{
    /**
     * 异常报错
     * @param       $errorData
     * @throws ApiException
     */
    public static function throwError($errorData)
    {
        // 分解错误码、错误信息
        $arr  = explode(':', (string)$errorData);
        $code = (int)$arr[0];
        $msg  = (string)$arr[1];

        throw new ApiException($code, $msg);
    }

    /**
     * 解码加密id
     * @param $idStr
     * @return int
     */
    public static function getDecodeId($idStr)
    {
        if (!is_numeric($idStr)) {
            $idArr = Hashids::decode($idStr);
            $id    = isset($idArr[0]) ? (int)$idArr[0] : 0;
        } else {
            $id = $idStr;
        }

        return $id;
    }

    /**
     * 加密id
     * @param $id
     * @return mixed
     */
    public static function getEncodeId($id)
    {
        return Hashids::encode($id);
    }

    /**
     * 加密批量bid
     * @param $bids
     * @return array
     */
    public static function getEncodeIds($bids)
    {
        return array_map(function ($bid) {
            return Hashids::encode($bid);
        }, $bids);
    }

    /**
     * 审核状态判断，true为审核状态，false默认
     * @param $package
     * @param $brand
     * @param $codeVersion
     * @return bool
     */
    public static function checkIsAudit($package, $brand, $codeVersion): bool
    {
        $audit = getProp(config('audit'), $package, []);
        if ($audit && $codeVersion === getProp($audit, 'codeVersion') && strtolower($brand) === getProp($audit, 'brand')) {
            return true;
        }

        return false;
    }

    /**
     * 获取redis的key
     * @param       $keyStr // 例如：wap.rank_page
     * @param array $args // 例如：[10, 2000]
     *
     * Utils::getCacheKey('wap.rank_page', [10, 2000])
     * Utils::getCacheKey('promotion.setStatus')
     * Utils::getCacheKey('promotion.infos', [2])
     *
     * @return string
     */
    public static function getCacheKey($keyStr, $args = [])
    {
        $stdStr = array_get(CacheKeys::$all, $keyStr, '');
        if ($args) {
            $stdStr = sprintf($stdStr, ...$args);
        }

        return $stdStr;
    }

    /**
     * 展示友好数字
     * @param $num
     * @return string
     */
    public static function showNiceNumber($num)
    {
        if ($num >= 10000) {
            $num = sprintf('%.1f', round($num / 10000 * 100) / 100) . '万';
        } elseif ($num >= 1000) {
            $num = sprintf('%.1f', round($num / 1000 * 100) / 100) . '千';
        }
        return $num;
    }

    /**
     * 根据日期获得这周的周一
     * @param        $date
     * @param int    $d
     * @param string $format
     * @return false|string
     */
    public static function firstOfWeek($date, $d = 0, $format = 'Ymd')
    {
        $now      = strtotime($date) - $d * 86400;    //当时的时间戳
        $number   = date("w", $now);  //当时是周几
        $number   = $number == 0 ? 7 : $number; //如遇周末,将0换成7
        $diff_day = $number - 1; //求到周一差几天
        return date($format, $now - ($diff_day * 60 * 60 * 24));
    }

    /**
     * 老原创的签名
     * @param $time
     * @return string
     */
    public static function getOldYcSign($time): string
    {
        $privateKey = env('EXTERNAL_PRIVATE_KEY');
        return md5(md5($time . $privateKey) . $privateKey);
    }

    /**
     * @param array $data
     * @param array $colHeaders
     * @param bool  $asString
     * @return bool|string
     */
    public static function toCSV(array $data, array $colHeaders = array(), $asString = false)
    {
        $stream = $asString ? fopen('php://temp/maxmemory', 'wb+') : fopen('php://output', 'wb');

        if (!empty($colHeaders)) {
            fputcsv($stream, $colHeaders);
        }

        foreach ($data as $record) {
            fputcsv($stream, $record);
        }

        if ($asString) {
            rewind($stream);
            $returnVal = stream_get_contents($stream);
            fclose($stream);
            return $returnVal;
        }

        fclose($stream);
        return true;
    }

    /**
     * 设备信息
     * @return string
     */
    public static function getDeviceType(): string
    {
        //全部变成小写字母
        $agent = strtolower(getProp($_SERVER, 'HTTP_USER_AGENT'));
        $type  = 'other';
        //分别进行判断
        if (strpos($agent, 'iphone') || strpos($agent, 'ipad')) {
            $type = 'ios';
        }

        if (strpos($agent, 'android')) {
            $type = 'android';
        }
        return $type;
    }

    /**
     * 是否在微信内打卡
     * @return bool
     */
    public static function isOpenedInWechat(): bool
    {
        //全部变成小写字母
        $agent = strtolower($_SERVER['HTTP_USER_AGENT']);

        //判断
        if (strpos($agent, 'micromessenger')) {
            return true;
        }

        return false;
    }

    /**
     * 组装meta数据
     * @param     $page
     * @param     $total
     * @param int $pageSize
     * @return array
     */
    public static function buildMeta($page, $total, $pageSize = 15): array
    {
        $lastPage = (int)ceil($total / $pageSize);
        return [
            'current_page'  => (int)$page,
            'next_page'     => ++$page,
            'last_page'     => $lastPage ?? 1,
            'per_page'      => (int)$pageSize,
            'total'         => (int)$total,
            'next_page_url' => '',
            'prev_page_url' => ''
        ];
    }

    /**
     * 随机书币
     * @param $coin
     * @param $num
     * @return array|bool
     * @throws \Exception
     */
    public static function getRandomCoin($coin, $num)
    {
        $result = [];
        if ($coin < $num || $coin < 1 || $num < 1) {
            return $result;
        }

        // 计算平均值
        $rem  = $coin % $num;
        $mean = ($coin - $rem) / $num;

        // 水平分割
        for ($i = 0; $i < $num; $i++) {
            $result[$i] = $mean;
        }
        // 水平分割后将取模多余部分分配给第一个红包
        $result[0] += $rem;

        // 随机分配金额
        for ($i = 0; $i < $num; $i++) {
            $r1  = random_int(0, $num - 1);
            $r2  = random_int(0, $num - 1);
            $per = random_int(1, 99) / 100;

            // 随机金额
            $mon = $result[$r1] - floor($result[$r1] * $per);
            if ($result[$r1] - $mon > 0) {
                // 减去随机金额
                $result[$r1] -= $mon;
                // 添加随机金额
                $result[$r2] += $mon;
            }
        }
        return $result;
    }

    /**
     * 将数据推送到rabbitMq
     * 参考：https://www.vckai.com/xiao-xi-dui-lie-rabbitmq-san-phpde-shi-yong-shi-li
     * @param        $messageBody
     * @param string $queue
     * @return bool
     */
    public static function pushDataToAppSyncMq($messageBody, $queue = 'ycsd_sync'): bool
    {
        $exchange    = $queue; // 交换器
        $routing_key = $queue;

        try {
            $host       = env('RABBITMQ_HOST');
            $port       = env('RABBITMQ_PORT');
            $user       = env('RABBITMQ_LOGIN');
            $passwd     = env('RABBITMQ_PASSWORD');
            $vhost      = env('RABBITMQ_VHOST');
            $connection = new AMQPStreamConnection($host, $port, $user, $passwd, $vhost);

            // 创建通道
            $channel = $connection->channel();

            /**
             * 创建队列(Queue)
             * name: hello         // 队列名称
             * passive: false      // 如果用户仅仅想查询某一个队列是否已存在，如果不存在，不想建立该队列，仍然可以调用queue.declare，
             *                          只不过需要将参数passive设为true，传给queue.declare，如果该队列已存在，则会返回true；
             *                          如果不存在，则会返回Error，但是不会创建新的队列。
             * durable: true       // 是不持久化， true ，表示持久化，会存盘，服务器重启仍然存在，false，非持久化
             * exclusive: false    // 是否排他，指定该选项为true则队列只对当前连接有效，连接断开后自动删除
             *  auto_delete: false // 是否自动删除，当最后一个消费者断开连接之后队列是否自动被删除
             */
            $channel->queue_declare($queue, true, false, false, false);

            /**
             * 创建交换机(Exchange)
             * name: vckai_exchange// 交换机名称
             * type: direct        // 交换机类型，分别为direct/fanout/topic，参考另外文章的Exchange Type说明。
             * passive: false      // 如果设置true存在则返回OK，否则就报错。设置false存在返回OK，不存在则自动创建
             * durable: false      // 是否持久化，设置false是存放到内存中的，RabbitMQ重启后会丢失
             * auto_delete: false  // 是否自动删除，当最后一个消费者断开连接之后队列是否自动被删除
             */
            // $channel->exchange_declare('', 'direct', true, false, false);

            // 绑定消息交换机和队列
            // $channel->queue_bind($queue, $exchange);

            /**
             * 创建AMQP消息类型
             * delivery_mode 消息是否持久化
             * AMQPMessage::DELIVERY_MODE_NON_PERSISTENT  不持久化
             * AMQPMessage::DELIVERY_MODE_PERSISTENT      持久化
             */
            $msg = new AMQPMessage($messageBody, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_NON_PERSISTENT]);

            /**
             * 发送消息
             * msg: $msg                // AMQP消息内容
             * exchange: vckai_exchange // 交换机名称
             * queue: hello             // 队列名称
             */
            $channel->basic_publish($msg, '', $routing_key);

            /**
             * 关闭链接
             */
            $channel->close();
            $connection->close();
        } catch (\Exception $e) {
            sendNotice('RabbitMq 连接失败 ' . $e->getMessage());
        }

        return true;
    }
}