<?php

declare(strict_types=1);

namespace app\common\library\token\driver;

use app\common\library\token\Driver;
use support\Redis as RedisConnection;

/**
 * Token Redis 驱动（提升鉴权接口等高频调用的性能）
 * @see Driver
 */
class Redis extends Driver
{
    protected array $options = [];

    public function __construct(array $options = [])
    {
        $this->options = array_merge([
            'prefix' => 'tk:',
            'expire' => 2592000,
        ], $options);
        $this->handler = RedisConnection::connection('default');
    }

    public function set(string $token, string $type, int $userId, ?int $expire = null): bool
    {
        if ($expire === null) {
            $expire = $this->options['expire'] ?? 2592000;
        }
        $expireTime = $expire !== 0 ? time() + $expire : 0;
        $key = $this->getKey($token);
        $data = [
            'token'       => $token,
            'type'        => $type,
            'user_id'     => $userId,
            'create_time' => time(),
            'expire_time' => $expireTime,
        ];
        $ttl = $expire !== 0 ? $expire : 365 * 86400;
        $this->handler->setEx($key, $ttl, json_encode($data));
        return true;
    }

    public function get(string $token): array
    {
        $key = $this->getKey($token);
        $raw = $this->handler->get($key);
        if ($raw === false || $raw === null) {
            return [];
        }
        $data = json_decode($raw, true);
        if (!is_array($data)) {
            return [];
        }
        $data['expires_in'] = $this->getExpiredIn($data['expire_time'] ?? 0);
        return $data;
    }

    public function check(string $token, string $type, int $userId): bool
    {
        $data = $this->get($token);
        if (!$data || ($data['expire_time'] && $data['expire_time'] <= time())) {
            return false;
        }
        return $data['type'] === $type && (int) $data['user_id'] === $userId;
    }

    public function delete(string $token): bool
    {
        $this->handler->del($this->getKey($token));
        return true;
    }

    public function clear(string $type, int $userId): bool
    {
        $pattern = $this->options['prefix'] . '*';
        $keys = $this->handler->keys($pattern);
        foreach ($keys as $key) {
            $raw = $this->handler->get($key);
            if ($raw !== false && $raw !== null) {
                $data = json_decode($raw, true);
                if (is_array($data) && ($data['type'] ?? '') === $type && (int) ($data['user_id'] ?? 0) === $userId) {
                    $this->handler->del($key);
                }
            }
        }
        return true;
    }

    private function getKey(string $token): string
    {
        return $this->options['prefix'] . $this->getEncryptedToken($token);
    }
}
