<?php

namespace app\common\model;

use app\BaseModel;
use app\common\arw\adjfut\src\Exception\ErrorMsg;
use app\common\exception\LoginTimeOut;
use app\common\model\User\User;
use think\facade\Request;
use think\helper\Arr;
use think\model\relation\HasOne;

/**
 * Token
 *
 * @date 2022-12-27
 * @example
 * @author admin
 * @since 1.0.0
 */
class Token extends BaseModel
{
    // 设置主键名
    protected $pk = 'token_guid';
    // 设置废弃字段
    protected $disuse = [];
    // 设置字段信息
    protected $schema = [
        'token_id' => 'int',
        'token_guid' => 'string',
        'user_guid' => 'string',
        'token_menu' => 'json',
        'token_api' => 'json',
        'token_exp_time' => 'datetime',
        'token_content' => 'string',
        'token_create_time' => 'datetime',
        'token_update_time' => 'datetime',
    ];
    // json数据字段
    protected $json = ['token_menu', 'token_api'];
    // 设置JSON数据返回数组
    protected $jsonAssoc = true;
    // 开启自动写入时间戳字段
    protected $autoWriteTimestamp = 'datetime';
    // 创建时间
    protected $createTime = 'token_create_time';
    // 修改时间
    protected $updateTime = 'token_update_time';
    /**
     * 当前用户
     *
     * @var User
     */
    private static $user = null;
    /**
     * 当前model
     *
     * @var self
     */
    private static $current = null;

    /**
     * 用户
     *
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    public function user(): hasOne
    {
        return $this->hasOne(User::class, 'user_guid', 'user_guid');
    }

    /**
     * 设置过期时间
     *
     * @param mixed $value
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    public function setTokenExpTimeAttr($value): string
    {
        return date('Y-m-d H:i:s', $value);
    }

    /**
     * 判断是否过期
     *
     * @return boolean
     * @date 2022-12-27
     * @example
     * @author admin
     * @since 1.0.0
     */
    public function getIsExpAttr(): bool
    {
        return time() >= $this->token_exp_timestamp;
    }

    /**
     * 获取过期时间戳
     *
     * @return integer
     * @date 2022-12-27
     * @example
     * @author admin
     * @since 1.0.0
     */
    public function getTokenExpTimestampAttr(): int
    {
        return strtotime($this->token_exp_time);
    }


    /**
     * 新增前
     *
     * @param self $model
     * @return void
     * @date 2022-12-27
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function onBeforeInsert(self $model): void
    {
        $model->token_content = $model->generateContent();
        $model->token_guid = self::generateGuid();
    }

    /**
     * 登陆
     * 如果当前存在有效token 更新过期时间
     * 如果token已过期 更新token和过期时间
     * 如果不存在token 新增数据
     *
     * @param string $guid 用户id
     * @param array $options 配置项
     * @param int $options[expTime] 过期时间(时间戳) 不传默认两小时
     * @param array $options[menu] 菜单
     * @param int $options[api] 接口
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function login(string $guid, array $options = []): self
    {
        $expTime = 0;
        $menu = [];
        $api = [];
        if (is_array($options)) {
            $expTime = Arr::get($options, 'expTime', 0);
            $menu = Arr::get($options, 'menu', []);
            $api = Arr::get($options, 'api', []);
        }
        if ($expTime == 0) {
            $expTime = time() + (24 * 60 * 60);
        }
        $data = [
            'token_exp_time' => $expTime
        ];
        if ($menu) {
            $data['token_menu'] = $menu;
        }
        if ($api) {
            $data['token_api'] = $api;
        }
        /**
         * @var Token
         */
        $model = self::where([
            'user_guid' => $guid,
        ])->find();
        // 数据不存在
        if (!$model) {
            return self::create($data + [
                'user_guid' => $guid,
            ]);
        }
        // token已过期
        if ($model->is_exp) {
            $data['token_content'] = $model->generateContent($model);
        }
        $model->save($data);
        return $model;
    }

    /**
     * 登出
     *
     * @date 2022-03-15
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function logout(): void
    {
        self::getCurrent()->delete();
    }

    /**
     * 获取请求token
     *
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function getRequestToken(): string
    {
        $tokenKey = 'token';
        $token = '';
        if (!$token && isset($_GET[$tokenKey])) {
            $token = $_GET[$tokenKey];
        }
        if (!$token && isset($_POST[$tokenKey])) {
            $token = $_POST[$tokenKey];
        }
        if (!$token && Request::param($tokenKey)) {
            $token = Request::param($tokenKey);
        }
        if (!$token && Request::header($tokenKey)) {
            $token = Request::header($tokenKey);
        }
        return $token;
    }

    /**
     * 获取当前用户信息
     *
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function getCurrentUser(): User
    {
        if (!self::$user) {
            $model = self::getCurrent();
            self::$user = $model->user;
            if (!self::$user) {
                throw new ErrorMsg("用户不存在", 1);
            }
        }
        return self::$user;
    }

    /**
     * 获取当前有效token
     *
     * @date 2022-02-28
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function getCurrent(): self
    {
        if (!self::$current) {
            $content = self::getRequestToken();
            if (!$content) {
                throw new LoginTimeOut("缺少token", 1);
            }
            $model = self::where([
                'token_content' => $content,
            ])->find();
            if (!$model) {
                throw new LoginTimeOut("未登录", 1);
            }
            if ($model->is_exp) {
                throw new LoginTimeOut("登录超时", 1);
            }
            self::$current = $model;
        }
        return self::$current;
    }

    /**
     * 判断是否登录
     *
     * @date 2022-05-14
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function isLogin(): bool
    {
        try {
            self::getCurrent();
            return true;
        } catch (LoginTimeOut $th) {
            return false;
        }
    }

    /**
     * 延长token
     * 过期前30分钟才会延期
     *
     * @date 2022-02-28
     * @example
     * @author admin
     * @since 1.0.0
     */
    public static function delay(): void
    {
        try {
            // 半小时
            $delay = (60 * 30);
            $model = self::getCurrent();
            $token_exp_time = $model->token_exp_timestamp;
            if (time() >= ($token_exp_time - $delay)) {
                self::login($model->user_guid);
            }
        } catch (LoginTimeOut $th) {
        }
    }

    /**
     * 生成token内容
     *
     * @date 2022-02-22
     * @example
     * @author admin
     * @since 1.0.0
     */
    private function generateContent(): string
    {
        return md5(join('-', [
            $this->token_exp_time,
            $this->user_guid,
        ]) . time());
    }
}
