<?php namespace App\Http\Controllers\Api;

use App\Exceptions\Api\VerifySignException;
use App\Exceptions\Api\LoginException;
use App\Exceptions\Api\ApiException;
use App\Facades\Servers\Encrypts\AccessToken;
use App\Facades\Servers\Encrypts\ApiSign;
use App\Http\Controllers\Controller;

/**
 * 接口控制器
 * 
 * @author 刘相欣
 * 
 * */
class Api extends Controller{

	/**
	 * 登录验证
	 * 
	 * @return	Int			$authcode		登录信息
	 * 
	 */
	public function checkLogin($authcode=''){
		// 默认接参
		$authcode											= $authcode ? $authcode : request('authcode','');
        $port                                               = request('port','');
		// 如果没有有登录信息
		if( !$authcode )									throw new LoginException("please_login");
		// 解码
		$decode												= AccessToken::decode($authcode);
		// 错误返回
		if( isset($decode['error']) )						throw new LoginException("login_error");
		// 如果没有过期时间,过期处理
		if( empty($decode['exp']) )							throw new LoginException("login_exprie");
		// 如果过期,过期处理
		if( $decode['exp'] <= time() )						throw new LoginException("login_exprie");
		// 登录的商户ID
		$loginCompanyId										= empty($decode['company_id']) ? 0 : $decode['company_id'];
		// 如果登录的商户不一致，提醒重新登录
		if( $loginCompanyId != request('company_id',0) )	throw new LoginException("please_login");
		// 从缓存获取登录数据
		$memAuthcode 										= (new Custom())->getLoginAuthCodeByUid($decode['sub'],$port);
		// 不存在登录信息，提示失败
		if( !$memAuthcode )									throw new LoginException("login_miss");
		// 如果是32位的话
		if( strlen($memAuthcode) == 32 )					$authcode = md5($authcode);
		// 如果存在登录数据，并且不是当前用户的缓存登录，提示失败
		if( $authcode != $memAuthcode )						throw new LoginException("login_miss");
		// 返回用户ID
		return												$decode['sub'];
	}

	/**
	 * 登录验证
	 * 
	 * @return	Int		用户ID
	 * 
	 */
	public function getUid($authcode=''){
		// 默认接参
		$authcode											= $authcode ? $authcode : request('authcode','');
		// 如果没有有登录信息
		if( !$authcode )									return 0;
		// 尝试执行
		try {
			// 解码
			$decode											= AccessToken::decode($authcode);
			// 错误返回
			if( isset($decode['error']) )					return 0;
			// 如果没有过期时间,过期处理
			if( empty($decode['exp']) )						return 0;
			// 如果过期,过期处理
			if( $decode['exp'] <= time() )					return 0;
			// 登录的商户ID
			$loginCompanyId									= empty($decode['company_id']) ? 0 : $decode['company_id'];
			// 如果登录的商户不一致，提醒重新登录
			if( $loginCompanyId != request('company_id',0)) return 0;
			// 返回用户ID
			return											$decode['sub'];
		} catch (\Throwable $th) {
			// 返回0
			return											0;
		}
	}

	/**
	 * 接口验签
	 * 
	 * @param		[Array]		$data		参与验证的参数
	 * 
	 * 
	 * */
	public function verify_sign($data=[]){
		// 获取参数
		$data									= $data ? $data : request()->all();
		// 没有签名
		if( empty($data['sign']) )				throw new VerifySignException('sign_miss');
		// 没有签名
		if( empty($data['appid']) )				throw new VerifySignException('appid_miss');
		// 传了key
		if( !empty($data['key']) )				throw new VerifySignException('appkey_no_allow');
		// 没有请求时间
		if( empty($data['timestamp'])  )        throw new VerifySignException('sign_timeout');
        // 请求过期
		if( abs(time() -$data['timestamp'] ) > 86400 ) throw new VerifySignException('sign_timeout');
        // 已使用过的签名不允许二次使用
		$this->checkSignUsed($data['sign']);
		// 从配置读取接口验签应用数据
		$verify 								= ['2DEA6A19BDCA383B'=>'FD6F87E89EABAF1063A849D971B68618'];
		// 是否有对应的appid
		if( !isset($verify[$data['appid']]) )	throw new VerifySignException('appid_nofund');
		// appkey
		$appkey									= $verify[$data['appid']];
		// 获得签名参数数据
		$sign									= $data['sign'];
		// 删除传入签名参数
		unset($data['sign']);
		// 删除上传文件的字段
		if( isset($data['file']))				unset($data['file']);
		// 键名字典排序
		ksort($data);
		// 空字符串
		$param									= '';
		// 循环参数对象，拼接url字符串
		foreach ( $data as $key => $value ) {
			// 如果键值为空（空字符串 空数组 空集合 空对象 字符串0 数字0 NULL FALSE），此参数省略；另 '000' 这类0组成的字符串 不为空
			if( strtolower($value)  == 'false' || strtolower($value)  == 'null' || empty($value) || ( is_numeric($value) && $value == 0 && substr_count($value,'.') == 1 ) ) continue;
			// 判断键值是否是数组，对象 ,如果是的话，键值转json字符串
			if( is_array( $value ) || is_object($value) ) $value = json_encode($value,JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
			// 如果键值为真则 键值转md5加密(此处小写) 为假则直接拼接,0特殊处理
			$param  .= '&'.$key.'='.$value;
		}
		// 转码
		$param									= ltrim($param,'&');
		// 解码
		$param									= rawurldecode($param);
		// 拼接私钥数据
		$param									.= '&key='.$appkey;
		// 比对签名，数据加密（MD5后转大写）,$param.'=>'.strtoupper(md5($param)).'=>'.$sign
		if( strtoupper(md5($param)) != $sign)	throw new VerifySignException('sign_error');
		// 验签通过
		return									['success'=>'验签成功'];
	}

	/**
	 * 判断签名是否已用
	 * 
	 * @param		[Array]		$data		参与验证的参数
	 * 
	 * 
	 * */
	public  function checkSignUsed($sign='')	{
		// 获取签名
		$is_used								= cache('ApiSign:'.$sign);
		// 如果签名已用
		if( $is_used )							throw new VerifySignException('sign_used');
		// 存储已经验证过的签名
		cache(['ApiSign:'.$sign=>$sign],60);
		// 成功返回
		return									['success'=>'签名可用'];
	}

	/**
	 * 接口验签
	 * 
	 * @param	Array		$data		参与验证的参数
	 * 
	 * 
	 * */
	public function sign($data)					{
		// 返回加密结果
		return									ApiSign::encode($data);
	}

    /**
     * 店铺验证
     *
     */
    public function checkShop($uid='', $shopId=''){
		// 商户ID
		$companyId										    = request('company_id',0);
        // 默认接参
        $shopId											    = $shopId ? $shopId : request('shop_id',0);
		// 访问记录
		$CustomShopRecord									= (new CustomShopRecord());
		// 如果没有店铺，获取用户记录最后访问的店铺
        if (!$shopId)  										$shopId = $uid ? $CustomShopRecord->query()->where(['company_id'=>$companyId,'custom_uid'=>$uid])->orderByDesc('visit_time')->value('shop_id') : $shopId;
		// 如果还是没有店铺，获取商户下的第一个店铺
		if (!$shopId)                                       $shopId = (new Shop())->query()->where(['company_id'=>$companyId])->orderBy('id')->value('id');
		// 如果有shopId
        if ( $shopId && $uid )                     			{
            // 判断是不是有对应的店铺数据
            $recordId                       				= $CustomShopRecord->where(['custom_uid'=>$uid,'company_id'=>$companyId,'shop_id'=>$shopId])->value('id');
            // 更新还是新增
            $recordId                       				? $CustomShopRecord->edit($recordId,['visit_time'=>time()]) : $CustomShopRecord->add(['custom_uid'=>$uid,'shop_id'=>$shopId,'company_id'=>$companyId,'visit_time'=>time()]); 
        }
        // 返回
        return												['shop_id'=>$shopId,'company_id'=>$companyId];
    }
}