# -*- coding: utf-8 -*-
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2023. All rights reserved.
import time
import os
import utils.common.log as logger
from utils.common.fic_base import StepBaseInterface
from utils.common.message import Message
from utils.DBAdapter.DBConnector import BaseOps
from utils.common.exception import HCCIException
from plugins.ResourceCheck.common.libs import common_libs
from plugins.ResourceCheck.common.libs import raid_libs


class CheckSlotsImpl:
    def __init__(self, project_id, pod_id):
        self.this_path = os.path.realpath(__file__ + '/../../')
        self.script_dst_dir = "/home/pkg/precheck/scripts/"
        self.db = BaseOps()
        self.project_id = project_id
        self.pod_id = pod_id
        self.cascaded_pod_id = int(pod_id) + 1
        self.error_list = []
        self.check_type = 'disk_info'

    @classmethod
    def check_slot_status(cls, bmc_ip, raid_obj, slot_list):
        slot_error = []
        slot_raid = []
        slot_empty = []
        for slot in slot_list:
            if raid_obj.check_disk_exist(slot):
                logger.info('BMC: %s, slot %s has a disk.' % (bmc_ip, slot))
                logger.info('Checking error of BMC: %s, slot %s.' % (bmc_ip, slot))
                ret, msg = raid_obj.check_disk_has_error(slot=slot)
                if ret:
                    msg = 'slot %s has error, msg: %s.' % (slot, msg)
                    logger.error(msg)
                    slot_error.append(slot)

                logger.info('Checking if BMC: %s slot %s is a member of RAID' % (bmc_ip, slot))
                ret = raid_obj.check_disk_in_raid(slot=slot)
                if ret:
                    msg = 'slot %s has been added to a raid.' % slot
                    logger.error(msg)
                    slot_raid.append(slot)
            else:
                msg = 'BMC: %s, slot %s has no disk.' % (bmc_ip, slot)
                logger.error(msg)
                slot_empty.append(slot)
        return slot_empty, slot_error, slot_raid

    @classmethod
    def get_zk_and_primary_node_list(cls, nodes_info):
        nodes_info_dic = {}
        primary_slot_total = 0
        zk_slot_total = 0
        bmc_ip_list_zk = []
        bmc_ip_list_prm = []
        for node in nodes_info:
            bmc_ip = node.get("bmc_ip")
            zk_slot = node.get("zk_slot")
            primary_slot = node.get("primary_slot")
            nodes_info_dic[bmc_ip] = {}
            if zk_slot:
                zk_slot_total = zk_slot_total + 1
                nodes_info_dic.get(bmc_ip)['zk_slot'] = zk_slot
                bmc_ip_list_zk.append(bmc_ip)
            if primary_slot:
                primary_slot_start, primary_slot_end = primary_slot.split('-')
                primary_slot_total = primary_slot_total + int(primary_slot_end) - int(primary_slot_start) + 1
                nodes_info_dic.get(bmc_ip)['prm_slot_start'] = primary_slot_start
                nodes_info_dic.get(bmc_ip)['prm_slot_end'] = primary_slot_end
                bmc_ip_list_prm.append(bmc_ip)
        if not bmc_ip_list_zk:
            logger.error('All hosts have no zk disk,please check LLD sheet device_information')
            raise HCCIException('113084')
        if not bmc_ip_list_prm:
            logger.error('All hosts have no primary storage disk,please check LLD sheet device_information')
            raise HCCIException('113085')
        if zk_slot_total < 3:
            logger.error('Number of MDC must be greater than 3, but only %d' % zk_slot_total)
            raise HCCIException('113086', zk_slot_total)
        if primary_slot_total < 12:
            logger.error('Number of primary disks must be greater than 12, but only %d' % primary_slot_total)
            raise HCCIException('113087', primary_slot_total)
        bmc_ip_list = list(set(bmc_ip_list_zk + bmc_ip_list_prm))
        return bmc_ip_list, nodes_info_dic

    def procedure(self):
        """分布式存储业务融合部署场景下检查磁盘槽位"""
        common_libs.check_flag()
        common_libs.purge_http_log()
        timestamp = str(int(time.time()))
        nodes = self.db.get_bmc_info_by_pod_id(self.pod_id) + self.db.get_bmc_info_by_pod_id(self.cascaded_pod_id)
        nodes_info = []
        for node in nodes:
            if node.get("bmc_role") not in ['kvm', 'dpdk']:
                continue
            if not node.get("zk_slot"):
                return
            nodes_info.append(node)

        bmc_ip_list, nodes_info_dic = self.get_zk_and_primary_node_list(nodes_info)

        supported_bmc_ip_list = self.get_support_raid_nodes(bmc_ip_list, timestamp)

        common_libs.check_all_result(bmc_ip_list, self.check_type, timestamp)

        self.check_slots(nodes_info_dic, supported_bmc_ip_list, timestamp)

        if self.error_list:
            common_libs.report_all_error(self.error_list)

    def check_slots(self, nodes_info_dic, supported_bmc_ip_list, timestamp):
        for bmc_ip in supported_bmc_ip_list:
            logger.info('Checking node: %s' % bmc_ip)
            zk_slot = nodes_info_dic.get(bmc_ip).get('zk_slot')
            prm_slot_start = int(nodes_info_dic.get(bmc_ip).get('prm_slot_start'))
            prm_slot_end = int(nodes_info_dic.get(bmc_ip).get('prm_slot_end'))
            raid_obj = raid_libs.CheckRAID(self.project_id, bmc_ip, self.check_type, timestamp)
            raid_obj.get_disk_info_all()
            # check slot
            logger.info('Checking zk and primary disk slots')
            slot_list = list(range(prm_slot_start, prm_slot_end))
            if zk_slot:
                zk_slot = int(zk_slot)
                slot_list = slot_list + [zk_slot]
            slot_empty, slot_error, slot_raid = self.check_slot_status(bmc_ip, raid_obj, slot_list)
            self.update_error_list(bmc_ip, slot_empty, slot_error, slot_raid)

    def update_error_list(self, bmc_ip, slot_empty, slot_error, slot_raid):
        if slot_error:
            self.error_list.append(('113083', str(slot_error), bmc_ip))
        if slot_empty:
            self.error_list.append(('113081', str(slot_empty), bmc_ip))
        if slot_raid:
            self.error_list.append(('113088', str(slot_raid), bmc_ip))

    def get_support_raid_nodes(self, bmc_ip_list, timestamp):
        # 从主机直接获取所有节点RAID卡型号、ID
        host_raid_obj = raid_libs.HostRAIDCls(self.project_id, bmc_ip_list)
        # 从raid_lib获取支持的RAID卡型号、ID、raid_check_lib
        supported_bmc_ip_list = []
        for bmc_ip in bmc_ip_list:
            logger.info('Checking RAID compatibility for node:%s' % bmc_ip)
            host_supported_ctl_info = host_raid_obj.check_disk_controller_model(bmc_ip)
            if not host_supported_ctl_info:
                host_raid_dev_list = host_raid_obj.get_host_raid_dev_info(bmc_ip)
                logger.error('RAID controller on node:%s is not supported. Model: %s' %
                             (bmc_ip, host_raid_dev_list))
                self.error_list.append(('113061', host_raid_dev_list, bmc_ip))
            else:
                logger.info('Create checking script for node: %s' % bmc_ip)
                raid_libs.CheckRAID(self.project_id, bmc_ip, self.check_type, timestamp).create_check_script()
                supported_bmc_ip_list.append(bmc_ip)
        return supported_bmc_ip_list


class CheckSlots(StepBaseInterface):
    def __init__(self, project_id, pod_id):
        super(CheckSlots, self).__init__(project_id, pod_id)
        self.project_id = project_id
        self.pod_id = pod_id
        self.implement = CheckSlotsImpl(self.project_id, self.pod_id)

    def pre_check(self, project_id, pod_id):
        """
        插件内部接口：执行安装前的资源预检查，该接口由execute接口调用，工具框架不会直接调用此接口。
        :param project_id:
        :param pod_id:
        :return:
        """
        pass

    def execute(self, project_id, pod_id):
        try:
            self.implement.procedure()
        except HCCIException as e:
            return Message(500, e)
        except Exception as e:
            return Message(500, e)
        return Message(200)

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

    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:
        """
        pass
