import functools
from unittest.mock import patch

import redis

import xhiveframework
from xhiveframework.tests.utils import XhiveFrameworkTestCase
from xhiveframework.utils import get_bench_id
from xhiveframework.utils.background_jobs import get_redis_conn
from xhiveframework.utils.redis_queue import RedisQueue


def version_tuple(version):
	return tuple(map(int, (version.split("."))))


def skip_if_redis_version_lt(version):
	def decorator(func):
		@functools.wraps(func)
		def wrapper(*args, **kwargs):
			conn = get_redis_conn()
			redis_version = conn.execute_command("info")["redis_version"]
			if version_tuple(redis_version) < version_tuple(version):
				return
			return func(*args, **kwargs)

		return wrapper

	return decorator


class TestRedisAuth(XhiveFrameworkTestCase):
	@skip_if_redis_version_lt("6.0")
	@patch.dict(xhiveframework.conf, {"bench_id": "test_bench", "use_rq_auth": False})
	def test_rq_gen_acllist(self):
		"""Make sure that ACL list is genrated"""
		acl_list = RedisQueue.gen_acl_list()
		self.assertEqual(acl_list[1]["bench"][0], get_bench_id())

	@skip_if_redis_version_lt("6.0")
	@patch.dict(xhiveframework.conf, {"bench_id": "test_bench", "use_rq_auth": False})
	def test_adding_redis_user(self):
		acl_list = RedisQueue.gen_acl_list()
		username, password = acl_list[1]["bench"]
		conn = get_redis_conn()

		conn.acl_deluser(username)
		_ = RedisQueue(conn).add_user(username, password)
		self.assertTrue(conn.acl_getuser(username))
		conn.acl_deluser(username)

	@skip_if_redis_version_lt("6.0")
	@patch.dict(xhiveframework.conf, {"bench_id": "test_bench", "use_rq_auth": False})
	def test_rq_namespace(self):
		"""Make sure that user can access only their respective namespace."""
		# Current bench ID
		bench_id = xhiveframework.conf.get("bench_id")
		conn = get_redis_conn()
		conn.set("rq:queue:test_bench1:abc", "value")
		conn.set(f"rq:queue:{bench_id}:abc", "value")

		# Create new Redis Queue user
		tmp_bench_id = "test_bench1"
		username, password = tmp_bench_id, "password1"
		conn.acl_deluser(username)
		xhiveframework.conf.update({"bench_id": tmp_bench_id})
		_ = RedisQueue(conn).add_user(username, password)
		test_bench1_conn = RedisQueue.get_connection(username, password)

		self.assertEqual(test_bench1_conn.get("rq:queue:test_bench1:abc"), b"value")

		# User should not be able to access queues apart from their bench queues
		with self.assertRaises(redis.exceptions.NoPermissionError):
			test_bench1_conn.get(f"rq:queue:{bench_id}:abc")

		xhiveframework.conf.update({"bench_id": bench_id})
		conn.acl_deluser(username)
