<?php

namespace Api\PhpServices\Session;

use Api\PhpServices\Session\Session;
use Api\PhpServices\Session\RedisSessionHandler;
use Api\PhpUtils\Http\Request;

class RedisSession extends Session
{
    protected $redis = null;
    protected $key = 'c071f6e5923a648a5ef88f32b6f738e1';

    /**
     * 从cookie开启session
     * @param boolean $readOnly    是否只读会话，如果是，则在读取session之后直接关闭session
     *                             只读会话，需要在session更改后，手动调用write()写入，不然更改后的会话不会保存
     * @param string $app
     * @return boolean
     */
    public function start($readOnly = true)
    {
        if ($this->started) {
            return true;
        }
        $sessionId = $_COOKIE[$this->getSessionName()] ?? '';
        if (empty($sessionId) || !$this->checkSessionId($sessionId)) {
            return false;
        }
        $this->sessionId = $sessionId;

        $options = [];
        if ($readOnly) {
            $options['read_and_close'] = true;
        }
        return $this->doStart($options);
    }

    /**
     * 创建session，多用于用户登录后创建session，用户会话包括：访客会话和登录会话
     * @param integer $userid
     * @param string $appid                   session所属app，默认为一点资讯
     * @param boolean $isLogin                是否登录
     * @return boolean
     */
    public function create($appid, $userid, $isLogin = false)
    {
        // 如果session已开启，需要关闭当前会话
        if ($this->started) {
            $this->close();
        }
        $this->setAppid($appid);

        if ($userid === null || $appid === null) {
            throw new \Exception("userid[{$userid}], or appid[{$appid}] is null");
        }
        $sessionId = $this->generateSessionId($appid, $userid, $isLogin);

        if (empty($sessionId)) {
            return false;
        }
        $this->sessionId = $sessionId;

        $options = [];
        return $this->doStart($options, [
            'userid' => $userid,
        ]);
    }

    /**
     * start session
     * @param array $options                session_start()支持的选项
     * @param array $params                 该方法使用的附加参数
     * @return boolean
     */
    protected function doStart(array $options = [], array $params = [])
    {
        // 注册session处理器
        $sessionHandler = new RedisSessionHandler();
        $sessionHandler->setExpire($this->getExpire($params['userid'] ?? null));
        $this->setHandler($sessionHandler, true);

        if (empty($options['serialize_handler'])) {
            $options['serialize_handler'] = 'php_serialize';
        }
        // 禁用session_start()默认cookie header发送
        $options['use_cookies'] = 0;
        //$options['cache_limiter'] = '';
        $this->setSessionId();
        $this->setSessionName();
        return parent::start($options);
    }

    /**
     * 生成用户sessionId
     * @param integer $userid
     * @param string $username
     * @param boolean $isLogin                是否登录
     * @return string
     */
    public function generateSessionId($appid, $userid, $isLogin = false)
    {
        $validSessionIds = $this->validSessionIds($appid, $userid, $isLogin);
        // 提取其中在用的session
        $liveSession = $this->extractLiveSessionIds($validSessionIds);
        if (empty($liveSession)) {
            return $validSessionIds[rand(0, count($validSessionIds) - 1)];
        }

        $usableSesssionIds = array_values(array_diff($validSessionIds, array_keys($liveSession)));
        if ($usableSesssionIds) {
            $sessionId = $usableSesssionIds[array_rand($usableSesssionIds, 1)];
        } else {
            //10个可用全部用完，随机选一个
            $sessionId = $validSessionIds[rand(0, count($validSessionIds) - 1)];
        }
        return $sessionId;
    }

    /**
     * 删除当前会话
     * @return boolean
     */
    public function deleteSelf()
    {
        return $this->delete($this->sessionId);
    }

    /**
     * 删除指定session的redis
     * @param array|string $sessionIds
     * @return boolean
     */
    public function delete($sessionIds)
    {
        if (empty($sessionIds)) {
            return true;
        }
        if (!is_array($sessionIds)) {
            $sessionIds = [$sessionIds];
        }

        // 如果当前session也被删除，需要先关闭当前session
        if (in_array($this->sessionId, $sessionIds)) {
            $this->close();
        }

        $url = $this->getUrl('delete');
        $params = ['sessionid' => array_unique($sessionIds), 'key' => $this->key];
        $result = (new Request())->post($url, $params, 0, 'json');
        if (isset($result['response']['code']) && $result['response']['code'] == 0 ) {
            return true;
        }
        return false;
    }

    /**
     * 删除用户所有会话
     * @param integer $userid
     * @param string $username
     * @param int $type                 0 所有session，1 访客session，2 非访客session
     * @return boolean
     */
    public function deleteByUser($appid, $userid, $type = 0)
    {
        $sessionIds = [];
        if ($type === 1 || $type === 0) {
            $sessionIds = array_merge($sessionIds, $this->validSessionIds($appid, $userid, false));
        }
        if ($type === 2 || $type === 0) {
            $sessionIds = array_merge($sessionIds, $this->validSessionIds($appid, $userid, true));
        }
        return $this->delete($sessionIds);
    }

    /**
     * 提取在用的session id
     * @param array $sessionIds
     * @return array
     */
    public function extractLiveSessionIds(array $sessionIds)
    {
        $data = [];
        $url = $this->getUrl('mget');
        $params = ['sessionid' => $sessionIds, 'key' => $this->key];
        $result = (new Request())->post($url, $params, 0, 'json');
        if (isset($result['response']['code']) && $result['response']['code'] == 0
            && isset($result['response']['result']) && !empty($result['response']['result'])
        ) {
            $liveSession = $result['response']['result'];
            foreach ($liveSession as $key => $value) {
                if (empty($value)) {
                    continue;
                }
                $data[$sessionIds[$key]] = is_string($value) ? unserialize($value) : $value;
            }
        }
        return $data;
    }

    /**
     * get yaconf interface.url
     * @return string
     */
    public function getUrl($param = '')
    {
        if (empty($param)){
            return "";
        }
        $env = \Yaf\Application::app()->environ() ?? "test";
        $key = 'interface.' . $env . '.service.session.' . $param;
        return \Yaconf::get($key);
    }
}
