import json
import time

import paramiko
import pymysql

from update.models import UpdateCommand, Cinema
from update.serializers import UpdateCommandSerializer
from django.db.models import Q
from time import sleep


class UpdateCommandUtil:
    def __init__(self):
        self.client = paramiko.SSHClient()
        self.channel = None
        self.model = UpdateCommand

    def connect(self, cinema_ip):
        cinema_config = {'hostname': cinema_ip, 'port': 22, 'username': 'root', 'password': 'cine123456'}
        self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        self.client.connect(**cinema_config)

    def disconnect(self):
        self.client.close()

    def get_all_cmd(self):
        return self.model.objects.filter(is_delete=False).order_by('id').all()

    def get_sys_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=True)).order_by('id').all()

    def get_no_sys_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=False)).order_by('run_num').all()

    def get_no_sys_setup_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=False) & Q(process='setup')).order_by('id').all()

    def get_no_sys_teardown_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=False) & Q(process='teardown')).order_by(
            'id').all()

    def get_no_sys_sql_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=False) & Q(process='sql')).order_by('id').all()

    def get_no_sys_client_cmd(self):
        return self.model.objects.filter(Q(is_delete=False) & Q(is_sys=False) & Q(process='client')).order_by(
            'id').all()

    def get_checked_cmd(self, cmd_list, is_delete='0'):
        sys_result = self.get_sys_cmd()
        checked_setup_result = self.model.objects.filter(
            Q(is_delete=is_delete) & Q(id__in=cmd_list) & Q(process='setup')).order_by('run_num').all()
        checked_teardown_result = self.model.objects.filter(
            Q(is_delete=is_delete) & Q(id__in=cmd_list) & Q(process='teardown')).order_by('run_num').all()
        sys_cmd = [{'desc': sys.desc, 'cmd': sys.command} for sys in sys_result]
        checked_setup_cmd = [{'desc': setup.desc, 'cmd': setup.command} for setup in checked_setup_result]
        checked_teardown_cmd = [{'desc': teardown.desc, 'cmd': teardown.command} for teardown in
                                checked_teardown_result]
        return {'sys': sys_cmd, 'setup': checked_setup_cmd, 'teardown': checked_teardown_cmd}

    def exec_cmd(self, cinema_ip, _exec_cmd_list):
        self.connect(cinema_ip)
        exec_output = []
        exec_list = []
        for exec_cmd in _exec_cmd_list:
            print('执行命令对象:', exec_cmd)
            stdin, stdout, stderr = self.client.exec_command(exec_cmd['cmd'])
            out = stdout.read().decode('utf-8')
            err = stderr.read().decode('utf-8')
            print('正确输出:', out)
            print('错误输出:', err)
            out_format = out.replace('\n', '<br/>').strip()
            err_format = err.replace('\n', '<br/>').strip()
            if err == '':
                if out == '':
                    if exec_cmd['desc'] not in exec_list:
                        exec_output.append(exec_cmd['desc'] + ':执行成功')
                        exec_list.append(exec_cmd['desc'])
                else:
                    exec_output.append(exec_cmd['desc'] + ':执行成功 <br/>' + out_format)
            else:
                if exec_cmd['desc'] in ['检出版本', '拉取代码']:
                    exec_output.append(
                        exec_cmd['desc'] + ':执行成功 <br/>' + out_format + '<br/>' + err_format)
                    continue
                if exec_cmd['desc'] in ['执行升级脚本']:
                    exec_output.append(exec_cmd['desc'] + ':执行成功')
                    continue
                else:
                    exec_output.append(exec_cmd['desc'] + ':执行失败 ' + err.replace('\n', '<br/>').strip())
                    return False, exec_output
        self.disconnect()
        print('输出结果:', exec_output)
        return True, '<br/>'.join(exec_output)

    def exec_cmd_by_type(self, cinema_ip, _type, cmd_list, short_release, is_delete='0'):
        print('需要执行的命令列表:', cmd_list)
        exec_cmd_list = []
        exec_cmd_data = self.get_checked_cmd(cmd_list, is_delete)
        if _type == 'setup':
            cmds = exec_cmd_data['sys'] + exec_cmd_data['setup']
        elif _type == 'teardown':
            cmds = exec_cmd_data['teardown']
        else:
            cmds = exec_cmd_data['sys']
        for cmd in cmds:
            if '<params>' in cmd['cmd']:
                cmd['cmd'] = cmd['cmd'].replace('<params>', short_release)
                exec_cmd_list.append(cmd)
            elif cmd['cmd'].startswith('<multi>'):
                cmd['cmd'] = cmd['cmd'].replace('<multi>', '')
                for c in cmd['cmd'].split('|||'):
                    exec_cmd_list.append({'desc': cmd['desc'], 'cmd': c})
            else:
                exec_cmd_list.append(cmd)
        return self.exec_cmd(cinema_ip, exec_cmd_list)


# 执行设置相关
class UpdateConfigUtil:
    def __init__(self, cinema_ip, checked_list, run_before_teardown):
        self.ip = cinema_ip
        self.checked_list = checked_list
        self.run_before_teardown = run_before_teardown

    def exec_config(self, short_release):
        cmd_list = self.get_all_exec_cmd()
        db_config = Cinema.objects.filter(ip=self.ip).values()[0]
        db_conn = pymysql.Connect(host=self.ip, port=3306, user=db_config['db_user'], passwd=db_config['db_pwd'],
                                  database='cine')
        db_cursor = db_conn.cursor()
        result_list = []
        for cmds in cmd_list:
            print('执行命令: ', cmds)
            for cmd in cmds['cmd']:
                r = db_cursor.execute(cmd)
                print("执行子命令: ", cmds['desc'], cmd, '结果:', '执行失败(设置无需修改)' if r == 0 else '执行成功')
                result_list.append(cmds['desc'] + (':执行失败(设置无需修改)' if r == 0 else ':执行成功'))
                db_conn.commit()

                if cmds['combo_cmd'] != 0:
                    update_cmd = UpdateCommandUtil()
                    result, setup_output = update_cmd.exec_cmd_by_type(self.ip, 'setup', [cmds['combo_cmd']],
                                                                       short_release, is_delete='1')
                    print('执行关联命令:', setup_output)

        db_cursor.close()
        db_conn.close()
        return '<br/>'.join(result_list)

    def get_all_exec_cmd(self):
        all_config_obj = UpdateCommand.objects.filter(
            Q(process='config') & Q(run=self.run_before_teardown) & Q(is_delete=False)).values()
        cmd_list = []
        print(all_config_obj)
        for config_item in all_config_obj:
            if config_item['id'] in self.checked_list:
                cmd_list.append({"desc": config_item['desc'], "cmd": json.loads(config_item['command'])['checked'],
                                 "combo_cmd": config_item['combo_cmd']})
            else:
                cmd_list.append({"desc": config_item['desc'], "cmd": json.loads(config_item['command'])['unchecked'],
                                 "combo_cmd": config_item['combo_cmd']})
        return cmd_list