<?php

declare (strict_types=1);

namespace app\api\middleware;

use thans\jwt\exception\JWTException;
use thans\jwt\exception\TokenBlacklistException;
use thans\jwt\exception\TokenBlacklistGracePeriodException;
use thans\jwt\exception\TokenExpiredException;
use thans\jwt\middleware\JWTAuth;
use think\exception\HttpException;

/**
 * JWT驗證刷新token機制
 * Class JWTToken
 * @package app\api\middleware
 */
class JWT extends JWTAuth
{
    /**
     * 刷新token
     * @param $request
     * @param \Closure $next
     * @return mixed
     * @throws JWTException
     * @throws TokenBlacklistException
     * @throws TokenBlacklistGracePeriodException
     */
    public function handle($request, \Closure $next): object
    {
        try {
            $payload = $this->auth->auth();
        } catch (TokenExpiredException $e) { // token過期
            // 嘗試刷新token，會將舊token加入黑名單
            try {
                $this->auth->setRefresh();
                $token = $this->auth->refresh();
                $payload = $this->auth->auth(false);
            } catch (TokenBlacklistGracePeriodException $e) {
                $payload = $this->auth->auth(false);
            } catch (JWTException $exception) {
                // 如果捕獲到此異常，即代表 refresh 也過期了，用户無法刷新令牌，需要重新登錄。
                throw new HttpException(401, $exception->getMessage());
            }
        } catch (TokenBlacklistGracePeriodException $e) { // 捕獲黑名單寬限期
            $payload = $this->auth->auth(false);
        } catch (TokenBlacklistException $e) { // 捕獲黑名單，退出登錄或者已經自動刷新，當前token就會被拉黑
            throw new HttpException(401, 'not login...');
        }

        $request->uid = $payload['user_id']->getValue();

        $response = $next($request);

        if (isset($token)) {
            $this->setAuthentication($response, $token);
        }

        return $response;
    }
}