# coding: utf-8
# django-redis>=5.0.0 поддерживает sentinel из коробки
# Все остальные либы (django-sentinel, django-redis-sentinel, django-redis-sentinel-redux) не завелись на python2
# Код ниже взят отсюда: https://a.yandex-team.ru/arc/trunk/arcadia/intranet/wiki/src/wiki/utils/sentinel_ipv6.py
from __future__ import absolute_import

import logging

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django_redis.client import DefaultClient
from redis.sentinel import Sentinel

DJANGO_REDIS_LOGGER = getattr(settings, 'DJANGO_REDIS_LOGGER', False)


class SentinelClient(DefaultClient):
    """
    Sentinel client object extending django-redis DefaultClient
    """

    def __init__(self, server, params, backend):
        """
        Slightly different logic than connection to multiple Redis servers.
        Reserve only one write and read descriptors, as they will be closed on exit anyway.
        """
        super(SentinelClient, self).__init__(server, params, backend)
        self._client_write = None
        self._client_read = None
        self._connection_string = server
        self.log = logging.getLogger((DJANGO_REDIS_LOGGER or __name__))

    def parse_connection_string(self, constring):
        """
        Parse connection string in format:
            master_name/sentinel_server:port,sentinel_server:port/db_id
        Returns master name, list of tuples with pair (host, port) and db_id
        """
        try:
            connection_params = constring.split('/')
            master_name = connection_params[0]
            servers = [host_port.split(':') for host_port in connection_params[1].split(',')]
            sentinel_hosts = [(host, int(port)) for host, port in servers]
            db = connection_params[2]
        except (ValueError, TypeError, IndexError):
            raise ImproperlyConfigured("Incorrect format '%s'" % (constring))

        return master_name, sentinel_hosts, db

    def get_client(self, write=True, tried=(), show_index=False):
        """
        Method used to obtain a raw redis client.
        This function is used by almost all cache backend
        operations to obtain a native redis client/connection
        instance.
        """
        self.log.debug('get_client called: write=%s', write)
        if write:
            if self._client_write is None:
                self._client_write = self.connect(write)

            if show_index:
                return self._client_write, 0
            else:
                return self._client_write

        if self._client_read is None:
            self._client_read = self.connect(write)

        if show_index:
            return self._client_read, 0
        else:
            return self._client_read

    def close(self, **kwargs):
        """
        Closing old connections, as master may change in time of inactivity.
        """
        self._close_master()
        self._close_readonly_slave()

    def _close_readonly_slave(self):
        self.log.debug('Closing slave')
        if self._client_write:
            self._client_write.connection_pool.disconnect()
            self.log.debug('client_write closed')

        del self._client_write
        self._client_write = None

    def _close_master(self):
        self.log.debug('Closing master')
        if self._client_read:
            self._client_read.connection_pool.disconnect()
            self.log.debug('client_read closed')

        del self._client_read
        self._client_read = None

    def connect(self, write=True, SentinelClass=None):
        """
        Creates a redis connection with connection pool.
        """
        if SentinelClass is None:
            SentinelClass = Sentinel

        self.log.debug('connect called: write=%s', write)
        cluster_name, sentinel_hosts, db = self.parse_connection_string(self._connection_string)
        sentinel_timeout = self._options.get('SENTINEL_TIMEOUT', 1)
        password = self._options.get('PASSWORD', None)

        sentinel = SentinelClass(sentinel_hosts, socket_timeout=sentinel_timeout, password=password)

        if write:
            return sentinel.master_for(cluster_name)
        else:
            return sentinel.slave_for(cluster_name)