# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import json
import traceback
from collections import defaultdict

import utils.common.log as logger
from utils.business.param_util import HandleParamCheck
from utils.business.param_util import ParamUtil as ParamService
from utils.business.unite_password.unite_pwd_api import UnitePwdApi
from utils.business.project_util import ProjectApi
from utils.business.vm_util import can_vm_pinged_to
from utils.security import crypt
from utils.common.exception import HCCIException
from plugins.DistributedStorage.common.rest_client import StorageSSHClient
from plugins.DistributedStorage.common.upgrade_operate import UpgradeOperate
from plugins.DistributedStorage.common.public_handle_new import RestPublicMethod
from plugins.DistributedStorage.common.constants import UnifyPwdInfo

COMPONENT_NAME = UnifyPwdInfo.COMPONENT_NAME
SUB_COMPONENT_NAME = UnifyPwdInfo.SUB_COMPONENT_NAME
DYNAMIC_CHECK_LIST = ['FSM_{}_active_ip', 'FSM_{}_active_user',
                      'FSM_{}_active_pwd', 'FSM_{}_active_root_pwd',
                      'FSM_{}_standby_ip', 'FSM_{}_standby_user',
                      'FSM_{}_standby_pwd', 'FSM_{}_standby_root_pwd',
                      'Storage_{}_AZ_node_info']


def params_config(project_id):
    """
    插件参数动态获取接口函数
    中英文错误信息需要通过code2Msg()函数获取,入参为错误码，返回值为
        元组形式:中文错误信息，英文错误信息, 中文建议信息，英文建议信息
    查询所有已经校验的参数 调用ParamService()类中get_need_check_cloud_params(project_id, service_name)方法查询已经校验的参数
    """
    logger.info('Start parameter verification task.')
    config_ins = ParamsConfig(project_id)
    suit_info = config_ins.project_api.get_storage80_info(project_id)
    for suit_id, _float_ip in suit_info.items():
        # 已经校验的参数float_ip,DM登录名、登陆密码
        already_check_list = ['FSM_{}_float_ip', 'FSM_{}_rest_user', 'FSM_{}_rest_pwd']
        already_check_params = [str(i).format(suit_id) for i in already_check_list]

        dynamic_param_keys = [str(i).format(suit_id) for i in DYNAMIC_CHECK_LIST]
        # fsm ip、普通用户名、密码、root密码校验
        config_ins.get_dynamic_param(already_check_params, dynamic_param_keys)

    return config_ins.check_result


class ParamsConfig(object):
    def __init__(self, project_id):
        self.project_id = project_id
        self.db_opt = ParamService()
        self.project_api = ProjectApi()
        self.region_id = self.project_api.get_project_info(project_id).get("region_id")
        self.check_data = self.db_opt.get_need_check_cloud_params(project_id, 'DistributedStorage')
        self.check_result = HandleParamCheck()
        self.opr = None
        self.rest_opr = None

    @staticmethod
    def check_ssh_connectivity(check_list, data_dict, check_result):
        """
        :param data_dict:
        :param check_result:
        :param check_list:
        :return:
        """
        check_pass_flag = True
        logger.info('Start check ssh connectivity.')
        try:
            ParamsConfig.do_check_ssh_connectivity(check_list, data_dict)
        except HCCIException as err:
            logger.error(err)
            logger.error(traceback.format_exc())
            check_result.set_check_result(param_keys=check_list, status=500, error_msg=err)
            check_pass_flag = False
        except Exception as ex:
            logger.error(ex)
            logger.error(traceback.format_exc())
            check_result.set_check_result(param_keys=check_list,
                                          status=500,
                                          error_msg=HCCIException(620050, str(ex)))
            check_pass_flag = False
        finally:
            logger.info('check_ssh_connectivity end.')

        return check_pass_flag

    @staticmethod
    def do_check_ssh_connectivity(check_list, data_dict):
        # 组合请求数据
        fs_args = dict()
        node_ip = data_dict.get(check_list[0])
        node_username = data_dict.get(check_list[1])
        node_password = crypt.decrypt(data_dict.get(check_list[2]))
        root_password = crypt.decrypt(data_dict.get(check_list[3]))
        fs_args['port'] = "22"
        try:
            ssh_client = StorageSSHClient(node_ip, node_username,
                                          node_password)
        except Exception as err:
            err_msg = "login {0} with {1} failed. exc {2}".format(
                node_ip, node_username, err)
            raise HCCIException(621009, err_msg) from err
        try:
            cmd_ret = ssh_client.send_cmd('su -', 'assword:', 20)
        except Exception as e:
            err_msg = "root password has expired. Host ip [%s]" % node_ip
            raise HCCIException(621010, err_msg) from e
        logger.info("rsp %s" % cmd_ret)
        try:
            cmd_ret = ssh_client.send_cmd(root_password, '#', 20, sensitive=True)
        except Exception as e:
            err_msg = "root user password is wrong. Host ip [%s]" % node_ip
            raise HCCIException(621010, err_msg) from e
        logger.info("rsp %s" % cmd_ret)

    @staticmethod
    def check_ping(check_list, check_result, data_dict):
        check_pass_flag = True
        ip_addr = data_dict.get(check_list[0], None)
        if ip_addr is None:
            err_msg = "Failed to obtain the unified passwd"
            logger.error("Failed to obtain the unified passwd.")
            check_result.set_check_result(check_list,
                                          500,
                                          HCCIException(621014, err_msg))
            return False
        result = can_vm_pinged_to(ip_addr)
        if not result:
            logger.error("IP({}) is unreachable.".format(ip_addr))
            logger.error("check ping ip {ip_list} error, "
                         "error: {result}".format(ip_list=ip_addr,
                                                  result=result))
            check_result.set_check_result(check_list,
                                          500,
                                          HCCIException(620054, ip_addr))
            check_pass_flag = False
        return check_pass_flag

    @staticmethod
    def get_dependent_fs_args(check_data, dynamic_param_keys, fs_args):
        fs_args["master_ip"] = check_data.get(dynamic_param_keys[0])
        fs_args["master_root_pwd"] = crypt.decrypt(check_data.get(dynamic_param_keys[3]))
        fs_args["slaver_ip"] = check_data.get(dynamic_param_keys[4])
        fs_args["slaver_root_pwd"] = crypt.decrypt(check_data.get(dynamic_param_keys[7]))

    @classmethod
    def extract_data(cls, accounts_info, dynamic_param_keys, fsm_ips):
        data = dict()
        for account in accounts_info.get('data'):
            if account.get('ip') in fsm_ips:
                offset = fsm_ips.index(account.get('ip')) * 4
                if account.get("accountName") == 'root':
                    data[dynamic_param_keys[0 + offset]] = account.get('ip')
                    data[dynamic_param_keys[3 + offset]] = crypt.encrypt(account.get('newPasswd'))
                else:
                    data[dynamic_param_keys[1 + offset]] = account.get("accountName")
                    data[dynamic_param_keys[2 + offset]] = crypt.encrypt(account.get('newPasswd'))
        return data

    @staticmethod
    def _update_cloud_params(db_opt, project_id, check_data):
        data = list()
        for key, value in check_data.items():
            data.append({key: value})
        db_opt.update_user_input_cloud_params_by_key(project_id, data)

    def get_dynamic_param(self, already_check_params, dynamic_param_keys):
        float_ip = self.check_data.get(already_check_params[0])
        username = self.check_data.get(already_check_params[1])
        password = self.check_data.get(already_check_params[2])
        port = '8088'
        fs_args = dict()
        fs_args["float_ip"] = float_ip
        fs_args["port"] = port
        fs_args["password"] = password
        self.opr = UpgradeOperate(fs_args)
        self.rest_opr = RestPublicMethod(project_id=self.project_id, fs_args=fs_args)
        status_code, error_code, error_des = self.opr.try_login(
            username, password)
        if status_code != 200 or error_code != 0:
            err_msg = "login failed, Detail:[status:%s,code:%s]%s" \
                      % (status_code, error_code, error_des)
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        ret_result, ret_data = self.opr.get_servers()
        fsm_ips = []
        zk_node_list = []
        is_manage_storage_flag = False
        for node in ret_data:
            if 'management' in node.get("role"):
                fsm_ips.append(node.get('management_ip'))
            if 'zk' in node.get("usage"):
                zk_node_list.append(node.get("management_ip"))
            if 'storage' in node.get("role") \
                    and 'compute' in node.get("role"):
                is_manage_storage_flag = True
        if len(fsm_ips) != 2 or len(zk_node_list) == 0:
            err_msg = "Failed to obtain the fsm host IP"
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        logger.info("Success to obtain the FSM IP: %s" % fsm_ips)
        logger.info("Success to obtain the zk node IP: %s" % zk_node_list)
        logger.info("Start to get unified passwd!")

        self.get_zk_node_infos(dynamic_param_keys, fsm_ips, is_manage_storage_flag, zk_node_list)
        self.get_fsm_node_infos(dynamic_param_keys, fsm_ips)

    def get_zk_node_infos(self, dynamic_param_keys, fsm_ips, is_manage_storage_flag, zk_node_list):
        if not is_manage_storage_flag:
            # 业务存储场景不需要获取节点账号密码
            return
        zk_data = self.get_dynamic_param_zk_pwd(self.project_id, self.region_id, zk_node_list, dynamic_param_keys)
        if not zk_data:
            err_msg = "Host[%s] unregistered unified pwd management or failed obtain the unified passwd" % fsm_ips
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return
        check_list = ["node_ip", "user", "user_pwd", "root_pwd"]
        check_res = True
        for key, value in zk_data.items():
            check_data = value
            check_data["node_ip"] = key
            check_res = self.check_node_connectivity(check_list, check_data, self.check_result)
            if not check_res:
                break
        if check_res:
            db_data = dict()
            key = dynamic_param_keys[-1]
            db_data[key] = json.dumps(zk_data)
            self._update_cloud_params(self.db_opt, self.project_id, db_data)

    def get_fsm_node_infos(self, dynamic_param_keys, fsm_ips):
        check_data = self.get_dynamic_param_pwd(self.project_id, self.region_id, fsm_ips, dynamic_param_keys)
        if not check_data:
            err_msg = "Host[%s] unregistered unified pwd management or failed obtain the unified passwd" % fsm_ips
            logger.error(err_msg)
            self._update_check_result(dynamic_param_keys, 500, HCCIException(620055, err_msg))
            return
        self._update_cloud_params(self.db_opt, self.project_id, check_data)

    def get_dynamic_param_pwd(self, project_id, region_id, fsm_ips, dynamic_param_keys):
        ret = None
        unite_pwd_api = UnitePwdApi(project_id)
        account_list = [('fsadmin', 1), ('root', 1)]
        accounts_info = unite_pwd_api.get_account_info(
            region_id, account_list, '', COMPONENT_NAME,
            SUB_COMPONENT_NAME)
        if not accounts_info.get('data'):
            err_msg = "Failed to obtain host:%s the unified passwd." % fsm_ips
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return ret
        return self.extract_data(accounts_info, dynamic_param_keys, fsm_ips)

    def get_fsm_node_infos_for_one_step_upgrade(self, dynamic_param_keys, fsm_ips):
        check_data = self.get_dynamic_param_pwd_for_one_step_upgrade(
            self.project_id, self.region_id, fsm_ips, dynamic_param_keys)
        if not check_data:
            err_msg = "Host[%s] unregistered unified pwd management or failed obtain the unified passwd" % fsm_ips
            logger.error(err_msg)
            raise HCCIException(620055, err_msg)
        return check_data

    def get_dynamic_param_pwd_for_one_step_upgrade(self, project_id, region_id, fsm_ips, dynamic_param_keys):
        unite_pwd_api = UnitePwdApi(project_id)
        account_list = [('fsadmin', 1), ('root', 1)]
        accounts_info = unite_pwd_api.get_account_info(region_id, account_list, '', COMPONENT_NAME, SUB_COMPONENT_NAME)
        if not accounts_info.get('data'):
            err_msg = "Failed to obtain host:%s the unified passwd." % fsm_ips
            raise HCCIException(620055, err_msg)
        return self.extract_data(accounts_info, dynamic_param_keys, fsm_ips)

    def get_dynamic_param_zk_pwd(self, project_id, region_id, zk_nodes, dynamic_param_keys):
        ret = None
        unite_pwd_api = UnitePwdApi(project_id)
        account_list = [('fsp', 1), ('root', 1)]
        accounts_info = unite_pwd_api.get_account_info(
            region_id, account_list, '', "FusionSphere", "openstack")
        if not accounts_info.get('data'):
            err_msg = "Failed to obtain host:%s the unified passwd." % zk_nodes
            self._update_check_result(dynamic_param_keys, 500,
                                      HCCIException(620055, err_msg))
            return ret
        zk_nodes_infos = defaultdict(dict)
        for account in accounts_info.get('data'):
            if account.get('ip') in zk_nodes:
                node_ip = account.get('ip')
                zk_nodes_infos[node_ip]["user"] = "fsp"
                if account.get("accountName") == 'root':
                    zk_nodes_infos[node_ip]["root_pwd"] = crypt.encrypt(account.get('newPasswd'))
                else:
                    zk_nodes_infos[node_ip]["user_pwd"] = crypt.encrypt(account.get('newPasswd'))
        return zk_nodes_infos

    def check_node_connectivity(self, check_list, data_dict, check_result):
        if not self.check_ping(check_list, check_result, data_dict):
            return False
        if self.check_ssh_connectivity(check_list, data_dict,
                                       check_result):
            check_result.set_check_result(param_keys=check_list,
                                          status=200)
            return True
        return False

    def _update_check_result(self, param_keys, status, error_msg):
        self.check_result.set_check_result(param_keys, status, error_msg)
