<?php
/**
 * FunAdmin
 * ============================================================================
 * 版权所有 2017-2028 FunAdmin，并保留所有权利。
 * 网站地址: https://www.FunAdmin.com
 * ----------------------------------------------------------------------------
 * 采用最新Thinkphp8实现
 * ============================================================================
 * Author: yuege
 * Date: 2019/9/22
 */
namespace fun\auth;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\exception\HttpResponseException;
use think\facade\Db;
use think\facade\Request;
use think\Response;

trait Send
{
    /**
     * 时间差
     * @var int
     */
    public  $timeDif = 10000;
    /**
     * 刷新有效期
     * @var float|int
     */
    public  $refreshExpires = 3600 * 24 * 30;   //刷新token过期时间
    /**
     * 有效期
     * @var float|int
     */
    public  $expires = 7200*12;
    /**
     * 多个用户表
     * @var string
     */
    public  $tableName = 'member';

    /**
     * redis 对象
     * @var
     */
    public  $redis ;
    /**
     * 客户端对象
     * @var
     */
    public  $client ;

    /**
     * 商戶ID
     * @var int
     */
    protected $merchant_id = 0;

    /**
     * 测试appid，正式请数据库进行相关验证
     */
    protected $appid = '';
    /**
     * appsecret
     */
    protected $appsecret = '';
    /**
     * JWT key
     * @var string
     */
    public $key = '';

    public $group = 'api';

    /**
     * 返沪格式
     * @var string
     */
    public  $responseType = 'json';
    /**
     * 操作成功返回的数据
     * @param string $msg 提示信息
     * @param mixed $data 要返回的数据
     * @param int $code 错误码，默认为1
     * @param string $type 输出类型
     * @param array $header 发送的 Header 信息
     */
    public  function success($msg = '', $data = null, $code = 200, $type = null, array $header = [], $options= [])
    {
        $this->result($msg, $data, $code, $type, $header,$options);
    }

    /**
     * 操作失败返回的数据
     * @param string $msg 提示信息
     * @param mixed $data 要返回的数据
     * @param int $code 错误码，默认为0
     * @param string $type 输出类型
     * @param array $header 发送的 Header 信息
     */
    public function error($msg = '', $data = null, $code = 404, $type = null, $header = [], $options=[])
    {
        $this->result($msg, $data, $code, $type, $header,$options);
    }

    protected  function result($msg, $data = null, $code = 404, $type = null,  $header = [], $options = [])
    {
        $result = [
            'code' => $code,
            'msg' => $msg,
            'time' => \think\facade\Request::instance()->server('REQUEST_TIME'),
            'data' => $data,
        ];
        // 如果未设置类型则自动判断
        $type = $type ? $type : (\think\facade\Request::param(config('var_jsonp_handler')) ? 'jsonp' : $this->responseType);
        // 发送头部信息
        foreach ($header as $name => $val) {
            if (is_null($val)) {
                header($name);
            } else {
                header($name . ':' . $val);
            }
        }
        $response =  Response::create($result, $type)->header($header)->options($options);
        throw new HttpResponseException($response);
    }

    protected function getClientData($where=[],$field='*'){

        return Db::name('oauth2_client')->where($where)->field($field)->find();
    }

    protected function checkToken($jwt)
    {
        try {

            JWT::$leeway = 60;//当前时间减去60，把时间留点余地
            $decoded = JWT::decode($jwt, new Key(md5(config('api.jwt_key')), 'HS256'));
            $jwtAuth = (array)$decoded;
            $clientInfo['access_token'] = $jwt;
            $clientInfo['member_id'] = $jwtAuth['member_id'];
            $clientInfo['appid'] = $jwtAuth['appid'];
        } catch (\Firebase\JWT\SignatureInvalidException $e) {  //签名不正确
            throw new \Exception ($e->getMessage());
        } catch (\Firebase\JWT\BeforeValidException $e) {  // 签名在某个时间点之后才能用
            throw new \Exception($e->getMessage());
        } catch (\Firebase\JWT\ExpiredException $e) {  // token过期
            throw new \Exception($e->getMessage());
        } catch (Exception $e) {  //其他错误
            throw new \Exception($e->getMessage());
        }
        return $clientInfo;

    }

    /**
     * 获取客户端
     * @return array|false
     */
    protected function getClient()
    {
        //获取头部信息
        $authorization = config('api.authentication') ? config('api.authentication') : 'Authorization';
        $authorizationHeader = Request::header($authorization);
        if (!$authorizationHeader) {
            throw new \Exception(lang('Invalid authorization token'));
        }
        try {
            //jwt
            $clientInfo = $this->checkToken($authorizationHeader);
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
        return $clientInfo;
    }
    /**
     * 检测当前控制器和方法是否匹配传递的数组
     *
     * @param array $arr 需要验证权限的数组
     * @return boolean
     */
    public function match($action = [])
    {
        $request = Request::instance();
        $action = is_array($action) ? $action : explode(',', $action);
        if (empty($action)) {
            return false;
        }
        $action = array_map('strtolower', $action);
        // 是否存在
        if (in_array(strtolower($request->action()), $action) || in_array('*', $action)) {
            return true;
        }
        // 没找到匹配
        return false;
    }

    /**
     * token
     * @param $memberInfo
     * @param $expires
     * @return string
     */
    protected function buildAccessToken($memberInfo,$expires)
    {
        $time = time(); //签发时间
        $expire = $time + $expires; //过期时间
        $scopes = 'role_access';
        if($expires==$this->refreshExpires)  $scopes = 'role_refresh';
        $token = array(
            "member_id" => $memberInfo['member_id'],
            'appid'=>$this->appid,
            'appsecret'=>$this->appsecret,
            "iss" => "funadmin.com",//签发组织
            "aud" => "funadmin", //签发作者
            "scopes" => $scopes, //刷新
            "iat" => $time,
            "nbf" => $time,
            "exp" => $expire,      //过期时间时间戳
        );
        return   JWT::encode($token,  $this->key, 'HS256');
    }

    /**
     * @param $membername
     * @param $password
     * @return array|mixed|void
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    protected function getMember($membername, $password)
    {
        $member = Db::name($this->tableName)
            ->where('status',1)
            ->where('username', $membername)
            ->whereOr('mobile', $membername)
            ->whereOr('email', $membername)
            ->field('id as member_id,password')
            ->limit(1)
            ->find();
        if ($member) {
            if (password_verify($password, $member['password'])) {
                unset($member['password']);
                return $member;
            } else {
                $this->error(lang('Password is not right'), [], 401);
            }
        } else {
            $this->error(lang('Account is not exist'), [], 401);
        }
    }

}

