from contextlib import closing
from business.dao.param.param_items.params_item import ParamsItem
from business.dao.param.utils.constant import ParamType
from business.dao.param.utils.metadata_updater import MetadataUpdaterImpl, ParamsInfo
from business.dao.param.utils.param_dao_utils import save_params_from_userinput_data_iter, \
    init_fusionstorage_os_install_status_info, init_fusionstorage_os_install_status_info_item, \
    delete_params_from_userinput_data_iter, delete_fusionstorage_os_install_status_info_item
from utils.DBAdapter.model import model
from utils.common import log as logger
from utils.DBAdapter.db_password import ParamPassword
from utils.security import crypt


class FusionStorageBusinessDao(ParamsItem):
    def query_params(self, **kwargs) -> dict:
        if kwargs.get("param_type") in (ParamType.USER_PARAMS, ParamType.ALL):
            return {
                "fusionstorage_business_server_list":
                    self._query_fusionstorage_params(**kwargs)}

    def preprocess_params(
            self, params_type: str, params: dict, run_convert=False):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            return self._preprocess_fusionstorage_params(params)

    def convert_params(self, params_type: str, params: dict, **kwargs):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            return self._convert_fusionstorage_params(params)

    def delete_params(self, params_type: str, **kwargs):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            return self._delete_fusionstorage_params()

    def save_params(self, params_type: str, params: dict, **kwargs):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            return self._save_fusionstorage_params(params)

    def delete_params_for_item(self, params_type: str, params: dict, **kwargs):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            delete_params_from_userinput_data_iter(params, 'new_fusionstorage_business_server_list',
                                                   remove_func=self._delete_fusionstorage_params_item,
                                                   remove_other_func=delete_fusionstorage_os_install_status_info_item,
                                                   pod_id=self._pod.get("pod_id"), project_id=self._project_id)

    def update_params(self, params_type: str, params: dict, **kwargs):
        if params_type in (ParamType.USER_PARAMS, ParamType.ALL):
            self._update_fusionstorage_params(params)

    def _update_fusionstorage_params(self, params: dict):
        try:
            save_params_from_userinput_data_iter(
                self._project_id, self._pod, params,
                "new_fusionstorage_business_server_list",
                self.add_install_os_list,
                add_params=True,
                remove_func=self._delete_fusionstorage_params_item,
                init_func=init_fusionstorage_os_install_status_info_item)
            logger.info("fusionstorage os info updated to db ok >>>>>>>>>>>>>>")
        except Exception as err:
            logger.error(
                "An unexpected error occurs when update fusion "
                "storage business params, %s" % str(
                    err))
            raise err

    def _delete_fusionstorage_params_item(self, params):
        bmc_ips = [item.get('bmc_ip', '') for item in params]
        with closing(self.get_db_session()) as session:
            for bmc_ip in bmc_ips:
                try:
                    session.query(model.InstallOsList).filter(
                        model.InstallOsList.pod_id == self._pod.get("pod_id"),
                        model.InstallOsList.bmc_ip == bmc_ip,
                    ).delete()
                except Exception as error:
                    logger.error('fail to delete item: %s, err is %s' % (
                        self._pod.get("pod_id"), error))
            session.commit()

    def _query_fusionstorage_params(self, **kwargs) -> list:
        try:
            # generate fusionstorage separate server list
            return self._construct_params_from_db(
                self.get_install_os_list_info(self._pod.get("pod_id"),
                                              'all'),
                self.construct_fusion_storage_param_list_from_db,
                is_encrypted=kwargs.get("is_encrypted")
            )
        except Exception as err:
            logger.error(
                "An unexpected error occurs when construct "
                "fusion storage params, %s" % str(
                    err))
            raise err

    def _preprocess_fusionstorage_params(self, params):
        try:
            MetadataUpdaterImpl(str(self._project_id), self._pod, ParamsInfo(
                params, ("fusionstorage_business_server_list", "new_fusionstorage_business_server_list"),
                self.get_install_os_list_info(self._pod.get("pod_id"), 'all'))).update_params()
        except Exception as err:
            logger.error(
                "Unexpected error occurs when update is_metadata_node "
                "for FusionStorage Business, %s" % str(
                    err))
            raise err

    def _save_fusionstorage_params(self, params: dict):
        try:
            save_params_from_userinput_data_iter(
                self._project_id, self._pod, params,
                "new_fusionstorage_business_server_list",
                self.add_install_os_list,
                remove_func=self._delete_fusionstorage_params,
                init_func=init_fusionstorage_os_install_status_info)
            logger.info("fusionstorage os info saved to db ok >>>>>>>>>>>>>>")
        except Exception as err:
            logger.error(
                "An unexpected error occurs when save fusion "
                "storage business params, %s" % str(
                    err))
            raise err

    def _convert_fusionstorage_params(self, params: dict, **kwargs):
        pass

    def _delete_fusionstorage_params(self):
        with closing(self.get_db_session()) as session:
            try:
                session.query(model.InstallOsList).filter(
                    model.InstallOsList.pod_id ==
                    self._pod.get("pod_id")).delete()
            except Exception as error:
                logger.error('fail to delete: %s, err is %s' % (
                    self._pod.get("pod_id"), error))
            session.commit()

    def add_install_os_list(self, project_id, pod_id, data):
        server_list = []
        for unit in data:
            server_unit = model.InstallOsList()
            server_unit.pod_id = pod_id
            server_unit.sn = unit.get("sn")
            # 20200730：按照HCSD规范，自己定义的设备型号均要求不带空格。这里的修改是为了适配集成设计工具输出的参数表带空格的场景。
            server_unit.equipment_model = unit.get("equipment_model").replace(
                ' ', '') if unit.get(
                "equipment_model") else unit.get("equipment_model")

            server_unit.deploy_component = unit.get("deploy_component", '')
            server_unit.hostname = unit.get("hostname")
            server_unit.bmc_ip = unit.get("bmc_ip")
            server_unit.bmc_name = unit.get("bmc_name", '')
            server_unit.bmc_passwd = crypt.encrypt(unit.get("bmc_passwd"))
            server_unit.rack_id = unit.get("rack_id", '')
            server_unit.management_plane = unit.get("management_plane", '')
            server_unit.iscsi_business_plane = unit.get("iscsi_business_plane",
                                                        '')
            server_unit.iscsi_business_plane_ip = unit.get(
                "iscsi_business_plane_ip", '')
            server_unit.storage_plane = unit.get("storage_plane", '')
            server_unit.storage_inner_plane = unit.get("storage_inner_plane",
                                                       '')
            server_unit.replication_plane = unit.get("replication_plane", '')
            server_unit.manageIp = unit.get("manageIp", '')
            server_unit.storageIp = unit.get("storageIp", '')
            server_unit.storageInnerIp = unit.get("storageInnerIp", '')
            server_unit.replication_plane_ip = unit.get("replication_plane_ip",
                                                        '')
            server_unit.arbitration_plane_ip = unit.get("arbitration_plane_ip",
                                                        '')
            server_unit.creuser = crypt.encrypt(unit.get("creuser"))
            server_unit.is_metadata_node = unit.get("is_metadata_node", '')
            server_unit.storage_pool_name_and_slot = unit.get(
                "storage_pool_name_and_slot")
            server_unit.storage_pool_type = unit.get("storage_pool_type")
            server_unit.storagepool_redundancy_policy = unit.get(
                "storagepool_redundancy_policy")
            server_unit.ec_ratio = unit.get("ec_ratio")
            if unit.get('ec_block_size'):
                server_unit.ec_block_size = int(unit.get('ec_block_size'))
            else:
                server_unit.ec_block_size = 0
            server_unit.ec_cache = unit.get("ec_cache")
            server_unit.ec_verify_fragments = unit.get("ec_verify_fragments",
                                                       "")
            server_unit.ec_data_fragments = unit.get("ec_data_fragments", "")
            server_unit.zk_slot = unit.get("zk_slot")
            server_unit.zk_type = unit.get("zk_type")
            server_unit.storagepool_slot = unit.get("storagepool_slot")
            server_unit.primary_slot = unit.get("primary_slot")
            server_unit.primary_type = unit.get("primary_type")
            server_unit.cache_type = unit.get("cache_type")
            server_unit.replication_cluster_meta_info = unit.get(
                "replication_cluster_meta_info", "")
            server_unit.replication_cluster_meta_type = unit.get(
                "replication_cluster_meta_type", "")
            server_unit.baseService = unit.get("baseService", '')
            server_unit.businessIp = unit.get("businessIp", '')
            server_unit.tempTemplate = unit.get("tempTemplate", '')
            server_unit.dfv_node_type = unit.get("dfv_node_type", '')
            server_list.append(server_unit)

        with closing(self.get_db_session()) as session:
            session.add_all(server_list)
            session.commit()

    def get_install_os_list_info(self, pod_id, component='osd'):
        with closing(self.get_db_session()) as session:
            rows = session.query(model.InstallOsList).filter(
                model.InstallOsList.pod_id == pod_id).all()
            return self._append_param_list(component, rows)

    @staticmethod
    def _append_param_list(component, rows):
        params_list = []
        if rows is not None and len(rows) != 0:
            for row in rows:
                dic = dict(row.__dict__)
                dic.pop('_sa_instance_state', None)
                dic["creuser"] = crypt.decrypt(dic["creuser"])
                dic["bmc_passwd"] = crypt.decrypt(dic["bmc_passwd"])
                if dic["deploy_component"] and \
                        component in dic["deploy_component"]:
                    params_list.append(dic)
                elif not dic["deploy_component"] and component == 'osd':
                    params_list.append(dic)
                elif component == 'all':
                    params_list.append(dic)
        return params_list

    @staticmethod
    def construct_fusion_storage_param_list_from_db(
            param_list: list, is_encrypted=True) -> list:
        """
        对数据库中的参数进行构造
        :param is_encrypted:
        :param param_list:
        :return:
        """
        constructed_param_list = []
        for node in param_list:
            constructed_param_list.append({
                "os_id": node["os_id"],
                "sn": node["sn"],
                "equipment_model": node["equipment_model"],
                "deploy_component": node.get("deploy_component"),
                "hostname": node["hostname"],
                "bmc_ip": node["bmc_ip"],
                "bmc_name": node.get("bmc_name"),
                "bmc_passwd": ParamPassword.hide() if is_encrypted
                else node.get('bmc_passwd', ''),
                "rack_id": node.get("rack_id", ''),
                "management_plane": node.get("management_plane"),
                "iscsi_business_plane": node.get("iscsi_business_plane"),
                "iscsi_business_plane_ip": node.get(
                    "iscsi_business_plane_ip"),
                "storage_plane": node.get("storage_plane"),
                "storage_inner_plane": node.get("storage_inner_plane"),
                "replication_plane": node.get("replication_plane"),
                "manageIp": node.get("manageIp"),
                "storageIp": node.get("storageIp"),
                "storageInnerIp": node.get("storageInnerIp"),
                "replication_plane_ip": node.get("replication_plane_ip"),
                "arbitration_plane_ip": node.get("arbitration_plane_ip"),
                "creuser": ParamPassword.hide() if is_encrypted
                else node.get('creuser', ''),
                "is_metadata_node": node["is_metadata_node"],
                "storage_pool_name_and_slot": node[
                    "storage_pool_name_and_slot"],
                "storage_pool_type": node["storage_pool_type"],
                "storagepool_redundancy_policy": node[
                    "storagepool_redundancy_policy"],
                "ec_ratio": node["ec_ratio"],
                "ec_block_size": node["ec_block_size"],
                "ec_cache": node["ec_cache"],
                "ec_verify_fragments": node["ec_verify_fragments"],
                "ec_data_fragments": node["ec_data_fragments"],
                "zk_slot": node["zk_slot"],
                "zk_type": node["zk_type"],
                "storagepool_slot": node["storagepool_slot"],
                "primary_slot": node["primary_slot"],
                "primary_type": node["primary_type"],
                "cache_type": node["cache_type"],
                "replication_cluster_meta_info": node.get(
                    "replication_cluster_meta_info", ""),
                "replication_cluster_meta_type": node.get(
                    "replication_cluster_meta_type"),
                "businessIp": node["businessIp"],
                "tempTemplate": node["tempTemplate"],
                "baseService": node["baseService"],
                "dfv_node_type": node.get("dfv_node_type", "")

            })
        return constructed_param_list
