<?php
// +----------------------------------------------------------------------
// | A3Mall
// +----------------------------------------------------------------------
// | Copyright (c) 2020 http://www.a3-mall.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: xzncit <158373108@qq.com>
// +----------------------------------------------------------------------

namespace app\common\library\jwt;

use DateTimeImmutable;
use app\common\exception\BaseException;
use Lcobucci\JWT\Token\Plain;
use Lcobucci\JWT\Validation\Validator;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use think\facade\Request;

/**
 * @package app\common\library\jwt
 * @class Token
 * @author xzncit 2023-08-23
 */
class Token {

    public static $moduleName = '';

    public static function setName($name){
        self::$moduleName = $name;
    }

    /**
     * @param $name
     * @return string
     */
    public static function getName($name){
        $moduleName = (empty(self::$moduleName) ? app('http')->getName() : self::$moduleName) . $name;
        $value = str_replace(['+','/'], ['',''], base64_encode($moduleName));
        return trim($value,'=');
    }

    /**
     * 设置Token
     * @param $name
     * @param $value
     * @param array $options
     * @return string
     */
    public static function set($name,$value,$options=[]){
        $build      = Config::get();
        $now        = new DateTimeImmutable();
        $time       = $now->format("U");
        $builder    = $build->builder()
            ->issuedBy(Config::getHost())
            ->permittedFor(Config::getHost())
            ->identifiedBy(Config::getSign())
            ->issuedAt($now)
            ->canOnlyBeUsedAfter($now->modify('-1 minute'))
            ->expiresAt($now->modify('+15 day'))
            ->withClaim(self::getName($name), str_replace("=", "", base64_encode(implode("|",[$value, $time, md5(Config::getSign() . $value . $time)]))));

        if(!empty($options)){
            if(count($options) <= 1){
                $options = [$options];
            }

            foreach($options as $key=>$item){
                $builder->withHeader($item["name"],$item["value"]);
            }
        }

        $token = $builder->getToken($build->signer(), $build->signingKey());
        return $token->toString();
    }

    /**
     * 检查是否登录
     * @return bool
     */
    public static function isAuth(){
        $token = Request::header("Authorization");
        if(empty($token)){
            $token = Request::param("Authorization","");
        }

        return !empty($token);
    }

    /**
     * 获取token
     * @param string $field
     * @param string $token
     * @return mixed
     * @throws BaseException
     */
    public static function get($field,$token=""){
        try{
            $build  = Config::get();
            $data   = $build->parser()->parse(Auth::value($token));
            $value  = self::validating(self::getName($field),$data);
            return Auth::parse($value)["value"];
        } catch (\Exception $ex) {
            throw $ex;
        }
    }

    /**
     * 验证token
     * @param string $field
     * @param mixed  $token
     * @return mixed|null
     * @throws BaseException
     */
    public static function validating($field,$token){
        $build  = Config::get();

        if(!($token instanceof Plain)){
            throw new BaseException("验证token未通过",401);
        }

        $validator = new Validator();
        $now       = new \DateTimeImmutable();

        // 检查token是否生效
        if(!$token->isMinimumTimeBefore($now)){
            throw new BaseException("token还未生效",401);
        }

        // 检查token是否过期
        if($token->isExpired($now)){
            throw new BaseException("token已过期",401);
        }

        // 检查sign
        if(!$validator->validate($token,new SignedWith($build->signer(), $build->signingKey()))){
            throw new BaseException("sign验证出错",401);
        }

        return $token->claims()->get($field);
    }

}