#coding: utf-8
#-------------------------------------------------------------------
# 宝塔Linux面板
#-------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
#-------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
#-------------------------------------------------------------------

# sqlite模型
#------------------------------
import os,sys,re,json,shutil,psutil,time
from databaseModel.base import databaseBase
import public,panelMssql,redis,random
try:
    from BTPanel import session
except :pass


class panelRedisDB():

    __DB_PASS = None
    __DB_USER = None
    __DB_PORT = 6379
    __DB_HOST = '127.0.0.1'
    __DB_CONN = None
    __DB_ERR = None

    __DB_CLOUD = None
    def __init__(self):
        self.__config = self.get_options(None)

    def redis_conn(self,db_idx = 0):

        if self.__DB_HOST in ['127.0.0.1','localhost']:
            if public.get_server_status('redis') <= 0: return False

        if not self.__DB_CLOUD:
            self.__DB_PASS = self.__config['requirepass']
            self.__DB_PORT = int(self.__config['port'])
        try:
            redis_pool = redis.ConnectionPool(host=self.__DB_HOST, port= self.__DB_PORT, password= self.__DB_PASS, db= db_idx)
            self.__DB_CONN = redis.Redis(connection_pool= redis_pool)
            return self.__DB_CONN
        except :
             self.__DB_ERR = public.get_error_info()
        return False


    def set_host(self,host,port,name,username,password,prefix = ''):
        self.__DB_HOST = host
        self.__DB_PORT = int(port)
        self.__DB_NAME = name
        if self.__DB_NAME: self.__DB_NAME = str(self.__DB_NAME)
        self.__DB_USER = str(username)
        self._USER = str(username)
        self.__DB_PASS = str(password)
        self.__DB_PREFIX = prefix
        self.__DB_CLOUD = 1
        return self


    #获取配置项
    def get_options(self,get = None):

        result = {}
        redis_conf = public.readFile("{}/redis/redis.windows.conf".format(public.get_soft_path()))
        if not redis_conf: return False

        keys = ["bind","port","timeout","maxclients","databases","requirepass","maxmemory"]
        for k in keys:
            v = ""
            rep = "\n%s\s+(.+)" % k
            group = re.search(rep,redis_conf)
            if not group:
                if k == "maxmemory":
                    v = "0"
                if k == "maxclients":
                    v = "10000"
                if k == "requirepass":
                    v = ""
            else:
                if k == "maxmemory":
                    v = int(group.group(1)) / 1024 / 1024
                else:
                    v = group.group(1)
            result[k] = v
        return result



class main(databaseBase):

    _db_max = 16  #最大redis数据库
    def __init__(self):
        pass


    def GetCloudServer(self,args):
        '''
            @name 获取远程服务器列表
            @author hwliang<2021-01-10>
            @return list
        '''
        return self.GetBaseCloudServer(args)


    def AddCloudServer(self,args):
        '''
        @添加远程数据库
        '''
        return self.AddBaseCloudServer(args)

    def RemoveCloudServer(self,args):
        '''
        @删除远程数据库
        '''
        return self.RemoveBaseCloudServer(args)

    def ModifyCloudServer(self,args):
        '''
        @修改远程数据库
        '''
        return self.ModifyBaseCloudServer(args)

    def get_obj_by_sid(self,sid = 0,conn_config = None):
        """
        @取mssql数据库对像 By sid
        @sid 数据库分类，0：本地
        """
        if type(sid) == str:
            try:
                sid = int(sid)
            except :sid = 0

        if sid:
            if not conn_config: conn_config = public.M('database_servers').where("id=?" ,sid).find()
            db_obj = panelRedisDB()

            try:
                db_obj = db_obj.set_host(conn_config['db_host'],conn_config['db_port'],None,conn_config['db_user'],conn_config['db_password'])
            except Exception as e:
                raise public.PanelError(e)
        else:
            db_obj = panelRedisDB()
        return db_obj



    def get_list(self,args):
        """
        @获取数据库列表
        @sql_type = redis
        """
        result = []
        self.sid = args.get('sid/d',0)
        for x in range(0,self._db_max):

            data = {}
            data['id'] = x
            data['name'] = 'DB{}'.format(x)

            try:
                redis_obj = self.get_obj_by_sid(self.sid).redis_conn(x)
                data['keynum'] = redis_obj.dbsize()
                if data['keynum'] > 0:
                    result.append(data)
            except :pass

        #result = sorted(result,key= lambda  x:x['keynum'],reverse=True)
        return result


    def set_redis_val(self,args):
        """
        @设置或修改指定值
        """

        self.sid = args.get('sid/d',0)
        if not 'name' in args or not 'val' in args:
            return public.returnMsg(False,'参数传递错误.');

        endtime = 0
        if 'endtime' in args : endtime = int(args.endtime)

        redis_obj = self.get_obj_by_sid(self.sid).redis_conn(args.db_idx)
        if endtime:
            redis_obj.set(args.name, args.val, endtime)
        else:
            redis_obj.set(args.name, args.val)
        public.set_module_logs('win_redis','set_redis_val',1)
        return public.returnMsg(True,'操作成功.');

    def del_redis_val(self,args):
        """
        @删除key值
        """
        self.sid = args.get('sid/d',0)
        if  not 'key' in args:
            return public.returnMsg(False,'参数传递错误.');

        redis_obj = self.get_obj_by_sid(self.sid).redis_conn(args.db_idx)
        redis_obj.delete(args.key)

        return public.returnMsg(True,'操作成功.');


    def clear_flushdb(self,args):
        """
        清空数据库
        @ids 清空数据库列表，不传则清空所有
        """
        self.sid = args.get('sid/d',0)
        ids = json.loads(args.ids)
        #ids = []
        if len(ids) == 0:
             for x in range(0,self._db_max):
                 ids.append(x)

        for x in ids:
            redis_obj = self.get_obj_by_sid(self.sid).redis_conn(x)
            redis_obj.flushdb()

        return public.returnMsg(True,'操作成功.');

    def get_db_keylist(self,args):
        """
        @获取指定数据库key集合
        """

        search = '*'
        if 'search' in args:
            search = "*" + args.search+"*"
            public.set_search_history('databases','redis',args.search)
        db_idx = args.db_idx
        self.sid = args.get('sid/d',0)

        redis_obj = self.get_obj_by_sid(self.sid).redis_conn(db_idx)
        try:
            keylist = sorted(redis_obj.keys(search))
        except :
            keylist = []


        info = {'p':1,'row':10,'count':len(keylist)}

        if hasattr(args,'limit'): info['row'] = int(args.limit)
        if hasattr(args,'p'): info['p']  = int(args['p'])

        import page
        #实例化分页类
        page = page.Page();

        info['uri']   = args
        info['return_js'] = ''
        if hasattr(args,'tojs'): info['return_js']   = args.tojs

        slist = keylist[(info['p']-1) * info['row']:info['p'] * info['row']]

        rdata = {}
        rdata['page'] = page.GetPage(info,'1,2,3,4,5,8')
        rdata['where'] = ''
        rdata['data'] = []

        idx = 0
        from cgi import html
        for key in slist:
            item = {}
            item['name'] = key.decode()

            item['endtime'] = redis_obj.ttl(key)
            if item['endtime'] == -1: item['endtime'] = 0

            item['type'] = redis_obj.type(key).decode()

            if item['type'] == 'string':
                item['val'] = redis_obj.get(key).decode()
            elif item['type'] == 'hash':
                item['val'] = redis_obj.hgetall(key)
            elif item['type'] == 'list':
                item['val'] = str(redis_obj.lrange(key, 0, -1))
            elif item['type'] == 'set':
                item['val'] = str(redis_obj.smembers(key))
            elif item['type'] == 'zset':
                item['val'] = str(redis_obj.zrange(key, 0, 1, withscores=True))
            else:
                item['val'] = ''
            try:
                item['len'] = redis_obj.strlen(key)
            except:
                item['len'] = len(item['val'])

            item['val'] = public.xsssec(item['val'])
            item['name'] = public.xsssec(item['name'])
            rdata['data'].append(item)
            idx += 1
        rdata['search_history'] = public.get_search_history('databases', 'redis')
        return rdata


    def ToBackup(self,args):
        """
        @备份数据库
        """

        self.sid = args.get('sid/d',0)

        redis_obj = self.get_obj_by_sid(self.sid).redis_conn(0)
        redis_obj.save()

        src_path = '{}/dump.rdb'.format(redis_obj.config_get()['dir'])
        if not os.path.exists(src_path):
            return public.returnMsg(False,'BACKUP_ERROR')

        backup_path = session['config']['backup_path'] + '/database/redis/'
        if not os.path.exists(backup_path): os.makedirs(backup_path)

        fileName = backup_path + str(self.sid) + '_db_' + time.strftime('%Y%m%d_%H%M%S',time.localtime()) +'.rdb'
        shutil.copyfile(src_path,fileName)
        if not os.path.exists(fileName):
            return public.returnMsg(False,'BACKUP_ERROR')

        return public.returnMsg(True, 'BACKUP_SUCCESS')

    def DelBackup(self,args):
        """
        @删除备份文件
        """
        file = args.file
        if os.path.exists(file): os.remove(file)

        return public.returnMsg(True, 'DEL_SUCCESS');

    def InputSql(self,get):
        """
        @导入数据库
        """
        file = get.file
        self.sid = get.get('sid/d',0)

        redis_obj = self.get_obj_by_sid(self.sid).redis_conn(0)

        rpath = redis_obj.config_get()['dir']
        dst_path = '{}/dump.rdb'.format(rpath)
        if os.path.exists(dst_path): os.remove(dst_path)
        shutil.copy2(file, dst_path)

        self.restart_services()
        if os.path.exists(dst_path):
            return public.returnMsg(True, '恢复成功.')
        return public.returnMsg(False, '恢复失败.')


    def get_backup_list(self,get):
        """
        @获取备份文件列表
        """
        search = ''
        if hasattr(get,'search'): search = get['search'].strip().lower();


        nlist = []
        cloud_list = {}
        for x in self.GetCloudServer({'type':'redis'}): cloud_list['id-' + str(x['id'])] = x

        path  = session['config']['backup_path'] + '/database/redis/'
        if not os.path.exists(path): os.makedirs(path)
        for name in os.listdir(path):
            if search:
                if name.lower().find(search) == -1: continue;

            arrs = name.split('_')

            filepath = '{}/{}'.format(path,name).replace('//','/')
            stat = os.stat(filepath)

            item = {}
            item['name'] = name
            item['filepath'] = filepath
            item['size'] = stat.st_size
            item['mtime'] = int(stat.st_mtime)
            item['sid'] = arrs[0]
            item['conn_config'] = {}
            try:
                item['conn_config'] = cloud_list['id-' + str(arrs[0])]
            except : pass

            nlist.append(item)
        return nlist



    def restart_services(self):
        """
        @重启服务
        """
        public.ExecShell('net stop redis')
        public.ExecShell('net start redis')
        return True


    def check_cloud_database_status(self,conn_config):
        """
        @检测远程数据库是否连接
        @conn_config 远程数据库配置，包含host port pwd等信息
        """
        try:

            sql_obj = panelRedisDB().set_host(conn_config['db_host'],conn_config['db_port'],conn_config['db_name'],conn_config['db_user'],conn_config['db_password'])
            keynum = sql_obj.redis_conn(0).dbsize()
            return True
        except Exception as ex:

            return public.returnMsg(False,ex)