<?php


namespace Library\DB;

use Redis;

class RedisSingle implements IRedis
{
    private Redis $db_single;

    private bool $is_pipe = false;

    private ?string $last_error = "";

    public function createRedis(array $info): bool
    {
        $this->db_single = new Redis();
        if (false == $this->db_single->pconnect($info[0], $info[1])) {
            $this->last_error = "link redis ip:" . $info[0] . " port:" . $info[1] . " fail";
            return false;
        }
        return true;
    }

    public function startPipe()
    {
        $this->is_pipe = true;
        $this->db_single->multi();
    }

    public function finishPipe()
    {
        $this->is_pipe = false;
        $this->db_single->exec();
    }

    public function getLastError(): string
    {
        return $this->last_error;
    }

    public function isPipe(): bool
    {
        return $this->is_pipe;
    }

    public function evalSha(&$ret, string $sha, array $param, int $key_num): bool
    {
        $ret = $this->db_single->evalSha($sha, $param, $key_num);
        return $this->checkRedisError();
    }

    public function hIncrBy(&$ret, array $param): bool
    {
        if (count($param) != 3) {
            $this->last_error = "hIncrBy param num error";
            return false;
        }
        $key = $param[0];
        $field = $param[1];
        $increment = $param[2];
        $ret = $this->db_single->hIncrBy($key, $field, $increment);
        return $this->checkRedisError();
    }

    public function exists(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "exists param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->exists($key);
        return $this->checkRedisError();
    }

    public function script(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "script param num error";
            return false;
        }
        $command = $param[0];
        $scriptInfo = $param[1];
        $ret = $this->db_single->script($command, $scriptInfo);
        return $this->checkRedisError();
    }

    public function del(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "del param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->del($key);
        return $this->checkRedisError();
    }

    public function keys(&$ret, array $param): bool
    {
        $this->last_error = "keys do not use " . json_encode($param);
        $ret = $this->last_error;
        return false;
    }

    public function set(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "set param num error";
            return false;
        }
        $key = $param[0];
        $val = $param[1];
        $ret = $this->db_single->set($key, $val);
        return $this->checkRedisError();
    }

    public function get(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "get param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->get($key);
        return $this->checkRedisError();
    }

    public function hExists(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "hExists param num error";
            return false;
        }
        $key = $param[0];
        $hashKey = $param[1];
        $ret = $this->db_single->hExists($key, $hashKey);
        return $this->checkRedisError();
    }

    public function hGet(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "hGet param num error";
            return false;
        }
        $key = $param[0];
        $hashKey = $param[1];
        $ret = $this->db_single->hGet($key, $hashKey);
        return $this->checkRedisError();
    }

    public function hMSet(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "hGet param num error";
            return false;
        }
        $key = $param[0];
        $hashData = $param[1];
        $ret = $this->db_single->hMSet($key, $hashData);
        return $this->checkRedisError();
    }

    public function hMGet(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "hMGet param num error";
            return false;
        }
        $key = $param[0];
        $hashData = $param[1];
        $result = $this->db_single->hMGet($key, $hashData);
        return $this->dealRedisResult($ret, $result);
    }

    public function expire(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "expire param num error";
            return false;
        }
        $key = $param[0];
        $time = $param[1];
        $result = $this->db_single->expire($key, $time);
        return $this->dealRedisResult($ret, $result);
    }

    public function hDel(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "hDel param num error";
            return false;
        }
        $key = $param[0];
        $hashData = $param[1];
        $ret = $this->db_single->hDel($key, ...$hashData);
        return $this->checkRedisError();
    }

    public function hGetAll(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "hGetAll param num error";
            return false;
        }
        $key = $param[0];
        $result = $this->db_single->hGetAll($key);
        return $this->dealRedisResult($ret, $result);
    }

    public function hLen(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "hLen param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->hLen($key);
        return $this->checkRedisError();
    }

    public function zScore(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "zScore param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $ret = $this->db_single->zScore($key, $term);
        return $this->checkRedisError();
    }

    public function BRPop(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "BRPop param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->brPop($key, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function zRange(&$ret, array $param): bool
    {
        if (count($param) != 4) {
            $this->last_error = "zRange param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $type = $param[3];
        $result = $this->db_single->zRange($key, $start, $stop, $type);
        return $this->dealRedisResult($ret, $result);
    }

    public function zRevRange(&$ret, array $param): bool
    {
        if (count($param) != 4) {
            $this->last_error = "zRevRange param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $type = $param[3];
        $result = $this->db_single->zRevRange($key, $start, $stop, $type);
        return $this->dealRedisResult($ret, $result);
    }

    public function zRangeByScore(&$ret, array $param): bool
    {
        if (count($param) != 4) {
            $this->last_error = "zRangeByScore param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $type = $param[3];
        $result = $this->db_single->zRangeByScore($key, $start, $stop, array('withscores' => $type));
        return $this->dealRedisResult($ret, $result);
    }

    public function zRevRangeByScore(&$ret, array $param): bool
    {
        if (count($param) != 4) {
            $this->last_error = "zRevRangeByScore param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $type = $param[3];
        $result = $this->db_single->zRevRangeByScore($key, $start, $stop, array('withscores' => $type));
        return $this->dealRedisResult($ret, $result);
    }

    public function zRemRangeByScore(&$ret, array $param): bool
    {
        if (count($param) != 3) {
            $this->last_error = "zRemRangeByScore param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $result = $this->db_single->zRemRangeByScore($key, $start, $stop);
        return $this->dealRedisResult($ret, $result);
    }

    public function zAdd(&$ret, array $param): bool
    {
        if (count($param) != 3) {
            $this->last_error = "zAdd param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $score = $param[2];
        $result = $this->db_single->zAdd($key, $score, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function sAdd(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "sAdd param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->sAdd($key, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function sRem(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "sRem param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->sRem($key, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function zRem(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "zRem param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->zRem($key, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function rPush(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "rPush param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->rPush($key, $term);
        return $this->dealRedisResult($ret, $result);
    }

    public function lRange(&$ret, array $param): bool
    {
        if (count($param) != 3) {
            $this->last_error = "lRange param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $result = $this->db_single->lRange($key, $start, $stop);
        return $this->dealRedisResult($ret, $result);
    }

    public function lTrim(&$ret, array $param): bool
    {
        if (count($param) != 3) {
            $this->last_error = "lTrim param num error";
            return false;
        }
        $key = $param[0];
        $start = $param[1];
        $stop = $param[2];
        $result = $this->db_single->lTrim($key, $start, $stop);
        return $this->dealRedisResult($ret, $result);
    }

    public function lPop(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "lPop param num error";
            return false;
        }
        $key = $param[0];
        $result = $this->db_single->lPop($key);
        return $this->dealRedisResult($ret, $result);
    }

    public function BLPop(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "BLPop param num error";
            return false;
        }
        $key = $param[0];
        $time = $param[1];
        $result = $this->db_single->blPop($key, $time);
        return $this->dealRedisResult($ret, $result);
    }

    public function lRem(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "lRem param num error";
            return false;
        }
        $key = $param[0];
        $term = $param[1];
        $result = $this->db_single->lRem($key, $term, 0);
        return $this->dealRedisResult($ret, $result);
    }

    public function zCard(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "zCard param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->zCard($key);
        return $this->checkRedisError();
    }

    public function sRandMember(&$ret, array $param): bool
    {
        if (count($param) != 2) {
            $this->last_error = "sRandMember param num error";
            return false;
        }
        $key = $param[0];
        $count = $param[1];
        $ret = $this->db_single->sRandMember($key, $count);
        return $this->checkRedisError();
    }

    public function sCard(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "sCard param num error";
            return false;
        }
        $key = $param[0];
        $ret = $this->db_single->sCard($key);
        return $this->checkRedisError();
    }

    public function sMembers(&$ret, array $param): bool
    {
        if (count($param) != 1) {
            $this->last_error = "sMembers param num error";
            return false;
        }
        $key = $param[0];
        $result = $this->db_single->sMembers($key);
        return $this->dealRedisResult($ret, $result);
    }

    public function ExistError(): bool
    {
        if ($this->db_single == null) {
            $this->last_error = "redis single null";
            return true;
        }
        return false;
    }

    private function checkRedisError(): bool
    {
        $this->last_error = $this->db_single->getLastError();
        if ($this->last_error == null) {
            return true;
        }
        return false;
    }

    private function dealRedisResult(&$ret, $result): bool
    {
        $this->last_error = $this->db_single->getLastError();
        if ($this->last_error != null) {
            return false;
        }
        if (is_array($result)) {
            $ret = json_encode($result);
        } else {
            $ret = $result;
        }
        return true;
    }
}