from django.core.exceptions import ValidationError
from django.utils.html import format_html
from IPy import IP
import itertools as it
import re
from django.db import connection


def my_custom_sql():
    """
    链接数据库查询库中所有资源池的IP地址
    :return: 所有资源池IP地址
    :type: list
    """
    row_list = []
    cursor = connection.cursor()
    cursor.execute("SELECT ip FROM django.polls_ipaddr")
    row = cursor.fetchall()
    for i in row:
        row_list.append(i[0])
    return row_list


def judge_legal_ip(ip):
    """
    正则匹配方法,判断一个字符串是否是合法IP地址
    :param ip:
    :return:
    """
    compile_ip = re.compile('^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
    if compile_ip.match(ip):
        return True
    else:
        return False


def redis_apply_text(apply_text, redis_type=None):
    """
    解析审批页面中输入的文本信息,并格式化输出
    :param apply_text: 审批页面中输入的原始文本信息
    :param redis_type: redis的运行模式,目前支持Standalone和Sentinel
    :return: 返回格式化后的文本信息dict
    """
    return_text = "输入规则错误请遵循如下规则: </br>" \
                  "1. standalone类型:</br>" \
                  "masterIp:masterPort:memSize(M)(例如:10.10.xx.xx:2048)</br>" \
                  "2. sentinel类型:</br>" \
                  "masterIp:masterPort:memSize(M):masterName:slaveIp:slavePort</br>" \
                  "sentinelIp1</br>" \
                  "sentinelIp2</br>" \
                  "sentinelIp3" \
                  "3. Cluster类型(集群各实例端口不建议大于50000): </br>" \
                  "master1Ip:master1Port:memSize(M):slave1Ip:slave1Port</br>" \
                  "master2Ip:master2Port:memSize(M):slave2Ip:slave2Port</br>" \
                  "master3Ip:master3Port:memSize(M):slave3Ip:slave3Port</br>"
    mysql_ip_row = my_custom_sql()
    if redis_type:
        if isinstance(apply_text, str) and redis_type == 'Redis-Standalone':
            redis_text_split = apply_text.split(":")
            try:
                if redis_text_split[0] not in mysql_ip_row:
                    raise ValidationError("服务器{0},不在资源池列表中".format(redis_text_split[0]))
                apply_text_dict = {
                    'redis_ip':  redis_text_split[0],
                    'redis_port': redis_text_split[1],
                    'redis_mem': redis_text_split[2]
                }
            except Exception as e:
                raise ValidationError("文本格式输入错误,{0}".format(e))
            return apply_text_dict
        elif redis_type == 'Redis-Sentinel':
            try:
                all_line = apply_text.split('\r\n')
                redis_ins = all_line.pop(0)
                all_redis_ins = redis_ins.split(":")
                redis_mem = all_redis_ins.pop(2)
                redis_master_name = all_redis_ins.pop(2)
                all_redis_ins_ip = all_redis_ins[::2]
                all_redis_ins_port = all_redis_ins[1::2]
                redis_master_ip_port = {all_redis_ins_ip.pop(0): all_redis_ins_port.pop(0)}
                redis_slave_ip_port_list = []
                for i in all_redis_ins_ip:
                    if i not in mysql_ip_row:
                        raise ValidationError("服务器{0},不在资源池列表中或文本格式不正确".format(i))
                    for p in all_redis_ins_port:
                        redis_ins_ip_port_dict = {}
                        redis_ins_ip_port_dict[i] = p
                        if {i: p} not in redis_slave_ip_port_list:
                            redis_slave_ip_port_list.append(redis_ins_ip_port_dict)
                redis_sentinel = [redis_sentinel for redis_sentinel in all_line if redis_sentinel != '']
                apply_text_dict = {
                    'model_type': 'Redis-Sentinel',
                    'redis_master_ip_port': redis_master_ip_port,
                    'redis_master_name': redis_master_name,
                    'redis_sentinel_ip_port': redis_sentinel,
                    'redis_sentinel_num': len(redis_sentinel) - 1,
                    'redis_slave_ip_port': redis_slave_ip_port_list,
                    'redis_mem': redis_mem
                }
                return apply_text_dict
            except Exception as e:
                raise ValidationError("文本格式输入错误,{0}".format(e))
        elif redis_type == 'Redis-Cluster':
            redis_text_split = apply_text.split("\r\n")
            text_list = []
            for redis_ins in redis_text_split:
                text_dict = {}
                redis_inline = redis_ins.split(":")
                redis_mem = redis_inline.pop(2)
                try:
                    all_redis_ins_ip = redis_inline[::2]
                    for i in all_redis_ins_ip:
                        if i not in mysql_ip_row:
                            raise ValidationError("服务器{0},不在资源池列表中".format(i))
                    all_redis_ins_port = redis_inline[1::2]
                    all_redis = list(zip(all_redis_ins_ip, all_redis_ins_port))
                    text_dict["redis_ip_port"] = all_redis
                    text_dict['redis_mem'] = redis_mem
                    text_list.append(text_dict)
                except IndexError as e:
                    pass
            return text_list
        else:
            raise ValidationError("输入格式校验错误,请核对文本规则")
    else:
        redis_text_split = apply_text.split(":")
        if len(redis_text_split) < 2:
            raise ValidationError(format_html(return_text))
        else:
            if '\r\n' not in apply_text:
                if len(apply_text.split(":")) != 3:
                    raise ValidationError("单机格式文本校验错误")
                else:
                    redis_ip_check = apply_text.split(":")[0]
                    redis_port_check = apply_text.split(":")[1]
                    try:
                        IP(redis_ip_check)
                        if redis_ip_check not in mysql_ip_row:
                            raise ValidationError("服务器{0},不在资源池列表中".format(redis_ip_check))
                        int(redis_port_check)
                        if len(redis_ip_check.split(".")) != 4:
                            raise ValidationError("单机格式文本校验错误, 请确认输入的IP是{0}".format(redis_ip_check))
                    except Exception as e:
                        raise ValidationError("单机格式文本校验错误, 请确认输入文本{0}".format(e))
            elif '\r\n\r\n' in apply_text:
                raise ValidationError("审批文本中存在多余的空行,请删除空行")
            else:
                all_line = apply_text.split('\r\n')
                # redis_ins = all_line.pop(0)
                # all_redis_ins = redis_ins.split(":")
                # all_redis_ins_ip = all_redis_ins[::2]
                # all_redis_ins_port = all_redis_ins[1::2]
                # redis_sentinel = [redis_sentinel for redis_sentinel in all_line if redis_sentinel != '']
                # redis_sentinel_ip = [sentinel_ip.split(":")[0] for sentinel_ip in redis_sentinel]
                # redis_sentinel_port = [sentinel_port.split(":")[1] for sentinel_port in redis_sentinel]
                all_ip_list = []
                for one_line in all_line:
                    one_line_part = one_line.split(":")
                    try:
                        for part in one_line_part:
                            if judge_legal_ip(part):
                                all_ip_list.append(part)
                    except ValueError:
                        pass
                try:
                    for ip in all_ip_list:
                        IP(ip)
                        if ip not in mysql_ip_row:
                            raise ValidationError("服务器{0},不在资源池列表中".format(ip))
                except Exception as e:
                    raise ValidationError("文本输入格式错误,请检查并纠正错误{0}".format(e))


def split_integer(m, n):
    """
    正对redis cluster的slot等份划分
    :param m: 16384
    :param n: 等份的份数
    :return:
    """
    assert n > 0
    quotient = int(m / n)
    remainder = m % n
    if remainder > 0:
        return [quotient] * (n - remainder) + [quotient + 1] * remainder
    if remainder < 0:
        return [quotient - 1] * -remainder + [quotient] * (n + remainder)
    return [quotient] * n


def slot_split_part(n):
    """
    根据slot等分的份数,格式化redis命令行添加slot所需的格式
    :param n: split_integer函数的返回结果
    :return:
    """
    return ['..'.join((str(i+1), str(j))) for i, j in zip([-1]+list(it.accumulate(n[:-1])), it.accumulate(n))]


def recreate_conf_file():
    """
    TODO:从平台外部导入的reids实例,获取配置文件的绝对路径
    :return:
    """
    pass