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

import utils.common.log as logger
from utils.common.message import Message
from utils.common.exception import HCCIException
from utils.common.fic_base import StepBaseInterface
from utils.business.param_util import ParamUtil
from utils.business.manageone_cmdb_util import ManageOneCmdbUtil
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.error.hcci_error_code import get_code_msg
from utils.business.storage.storage_tool import StorageTool
from utils.business.project_condition_utils import get_project_condition_boolean as get_cont
from plugins.DistributedStorage.utils.common.deploy_constant import DeployConstant
from plugins.DistributedStorage.logic.deploy_operate import DeployOperate


class CheckExpIpConflictInf(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super().__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id

    def pre_check(self, project_id, pod_id):
        """
        插件内部接口：
        :param project_id:
        :param pod_id:
        :return:
        """
        return Message()

    def execute(self, project_id, pod_id):
        """
        标准调用接口：
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        try:
            ExpIpConflictCheck(self.project_id, self.pod_id).procedure()
        except HCCIException as e1:
            return Message(500, e1)
        except Exception as e2:
            err_msg = traceback.format_exc()
            logger.error('ERROR: {}, details: {}'.format(e2, err_msg))
            return Message(500, HCCIException(627207, err_msg))
        return Message(200)

    def rollback(self, project_id, pod_id):
        """
        标准调用接口：执行回滚
        :param project_id:
        :param pod_id:
        :return:Message类对象
        """
        return Message()

    def retry(self, project_id, pod_id):
        """
        标准调用接口：重试
        :return: Message类对象
        """
        return self.execute(project_id, pod_id)

    def check(self, project_id, pod_id):
        """
        标准调用接口：重试
        :param project_id:
        :param pod_id:
        :return:
        """
        return Message()


class ExpIpConflictCheck(object):
    def __init__(self, project_id, pod_id):
        self.project_id = project_id
        self.pod_id = pod_id
        self.db = BaseOps()
        self.param_util = ParamUtil()
        self.mo_cmdb = ManageOneCmdbUtil(self.project_id, self.pod_id)
        self.storage_tool = StorageTool(self.project_id, self.pod_id, self.db)
        self.get_param = self.db.get_user_input_cloud_param_by_key
        self.conflict_dic = collections.defaultdict(list)

    def procedure(self):
        """
        查询已有集群中，所有平面IP，是否与规划的IP range冲突
        场景：扩AZ复用、新增业务存储-新增池、新增节点、新增复制集群、扩可选云服务(CSDR/CSHA)复制分离
        """
        # 通过工步条件，限制工程场景
        # 1、从LLD表拿参数
        # 2、rest接口拿已有平面
        # 3、判断网路平面与已有IP是否共子网，不共子网，跳过；否则，判断IP范围是否冲突。
        logger.info('Check to network plane')
        lld_net_info_dic = self.get_network_plane_params_from_lld()
        cluster_network_data = self.get_exist_network_data_from_cluster()
        cluster_network_data.extend(self.get_deploy_node_infos_from_cmdb())

        logger.info('Starting to check to network plane')
        for item in lld_net_info_dic.items():
            logger.info('Current LLD network info: {}'.format(item))
            for cluster_node_info in cluster_network_data:
                self.check_ip_addr_of_cluster_node(cluster_node_info, item)

        logger.info('Check result:{}'.format(self.conflict_dic))
        if self.conflict_dic:
            conflict_ip = [set(tmp_list) for tmp_list in list(self.conflict_dic.values())]
            logger.error(get_code_msg(627623) % (list(self.conflict_dic.keys()), conflict_ip))
            raise HCCIException(627623, list(self.conflict_dic.keys()), conflict_ip)

    def check_ip_addr_of_cluster_node(self, cluster_node_info, item):
        range_key, (range_value, mask_value) = item
        ip_address_list = cluster_node_info.get('ip_address_list')
        ip_start, ip_end = range_value.split('-')
        sub_mask_obj = IPy.IP(ip_start).make_net(mask_value)
        for ip_info in ip_address_list:
            ip_address = ip_info.get('ip_address')
            # LLD表网络平面range_value与存在的ip_address不共子网，直接跳过
            if ip_address not in sub_mask_obj:
                continue
            # 共子网，需检查IP范围是否与存在的IP是否冲突，比如：range: 192.168.1.20-192.168.1.200，exist: 192.168.1.25
            if IPy.IP(ip_start).int() <= IPy.IP(ip_address).int() <= IPy.IP(ip_end).int():
                self.conflict_dic[range_key].append(ip_address)

    def get_network_plane_params_from_lld(self):
        logger.info('Get network plane parameters from LLD')
        net_info_dic = {
            "fusionstorage_manage_range":
                (self.get_param(self.project_id, "fusionstorage_manage_range"),
                 self.get_param(self.project_id, "fusionstorage_manage_netmask")),
            "fusionstorage_service_range":
                (self.get_param(self.project_id, "fusionstorage_service_range"),
                 self.get_param(self.project_id, "fusionstorage_service_netmask"))
        }
        if get_cont(self.project_id, "DRStorage_TFB_Sep&(CSDRStorage_TFB|CSHAStorage_TFB)"):
            rep_info_lst = {
                "fusionstorage_rep_range1":
                    (self.get_param(self.project_id, "fusionstorage_rep_range1"),
                     self.get_param(self.project_id, "fusionstorage_rep_netmask1")),
                "fusionstorage_rep_range2":
                    (self.get_param(self.project_id, "fusionstorage_rep_range2"),
                     self.get_param(self.project_id, "fusionstorage_rep_netmask2"))
            }
            net_info_dic.update(rep_info_lst)
            if get_cont(self.project_id, "CSHAStorage_TFB"):
                arb_info_dic = {"fusionstorage_arb_range": (self.get_param(self.project_id, "fusionstorage_arb_range"),
                                self.get_param(self.project_id, "fusionstorage_arb_netmask"))}
                net_info_dic.update(arb_info_dic)
        if get_cont(self.project_id, "TenantStorBackendNetSep"):
            inner_dic = {"fusionstorage_inner_range": (self.get_param(self.project_id, "fusionstorage_inner_range"),
                         self.get_param(self.project_id, "fusionstorage_inner_netmask"))}
            net_info_dic.update(inner_dic)
        return net_info_dic

    def get_exist_network_data_from_cluster(self):
        if get_cont(self.project_id, "(ExpansionAZ_KVM|ExpansionAZ_BMS)&TenantStorFB80"):
            return []
        logger.info('Querying Login Information')
        fsm_args_lst = self.storage_tool.get_storage_float_ip()
        if get_cont(self.project_id, "!DRStorage_TFB_PD&DRStorage_TFB_Sep&(CSDRStorage_TFB|CSHAStorage_TFB)"):
            logger.info('Querying DR Site Login Information')
            disaster_float_ip = self.get_param(self.project_id, "disaster_tolerant_storage_fsm_ip")
            fsm_args_lst = list(filter(lambda x: x["float_ip"] == disaster_float_ip, fsm_args_lst))
        if not fsm_args_lst:
            err_msg = 'Failed to query the DeviceManager login information from LLD'
            logger.error(err_msg)
            raise HCCIException(627622, err_msg)

        logger.info('Get network plane parameters from DistributedStorage cluster')
        fsm_args = fsm_args_lst[0]
        float_ip = fsm_args.get("float_ip")
        opr = DeployOperate(float_ip=float_ip)
        opr.login(DeployConstant.DM_LOGIN_USER, fsm_args.get("portal_pwd"))
        try:
            cluster_network_info = opr.get_cluster_network_info()
        except Exception as e:
            logger.error('Error:{}'.format(e))
            raise e
        finally:
            opr.logout()
        return cluster_network_info

    def get_deploy_node_infos_from_cmdb(self):
        """
        :return: list(dict)
        """
        region_code = self.param_util.get_param_value(
            self.pod_id,
            service_name=DeployConstant.CLOUD_SERVICE_NAME,
            param_key=DeployConstant.CLOUD_SERVICE_PARAMS_KEY
        )
        logger.info("Region code:{}".format(region_code))
        deploy_node_infos_list = self.mo_cmdb.get_deploy_node_info(
            region_code,
            index_name=DeployConstant.CLOUD_SERVICE_INDEX
        )
        logger.info("Deploy node information:{}".format(deploy_node_infos_list))
        ret = []
        for node_info in deploy_node_infos_list:
            node_name = node_info.get("name")
            if node_name and node_name.startswith("FusionStorageManager_dsware-server"):
                continue
            ip_addr_list = node_info.get("ipAddresses")
            if not ip_addr_list:
                continue
            for cur_id_dict in ip_addr_list:
                cur_ip = cur_id_dict.get("ip")
                if not cur_ip:
                    continue
                ret.append({"ip_address": cur_ip})
        logger.info("Results from cmdb:{}".format(ret))
        return [{"ip_address_list": ret}]
