import re
import subprocess

from netbox_agent.misc import get_vendor
from netbox_agent.raid.base import Raid, RaidController

REGEXP_CONTROLLER_HP = re.compile(r'Smart Array ([a-zA-Z0-9- ]+) in Slot ([0-9]+)')


def _get_indentation(string):
    """Return the number of spaces before the current line."""
    return len(string) - len(string.lstrip(' '))


def _get_key_value(string):
    """Return the (key, value) as a tuple from a string."""
    # Normally all properties look like this:
    #   Unique Identifier: 600508B1001CE4ACF473EE9C826230FF
    #   Disk Name: /dev/sda
    #   Mount Points: None
    key = ''
    value = ''
    try:
        key, value = string.split(':')
    except ValueError:
        # This handles the case when the property of a logical drive
        # returned is as follows. Here we cannot split by ':' because
        # the disk id has colon in it. So if this is about disk,
        # then strip it accordingly.
        #   Mirror Group 0: physicaldrive 6I:1:5
        string = string.lstrip(' ')
        if string.startswith('physicaldrive'):
            fields = string.split(' ')
            key = fields[0]
            value = fields[1]
        else:
            # TODO(rameshg87): Check if this ever occurs.
            return None, None

    return key.lstrip(' ').rstrip(' '), value.lstrip(' ').rstrip(' ')


def _get_dict(lines, start_index, indentation):
    """Recursive function for parsing hpssacli/ssacli output."""

    info = {}
    current_item = None

    i = start_index
    while i < len(lines):
        current_line = lines[i]
        if current_line.startswith('Note:'):
            i = i + 1
            continue

        current_line_indentation = _get_indentation(current_line)
        if current_line_indentation == indentation:
            current_item = current_line.lstrip(' ')

            info[current_item] = {}
            i = i + 1
            continue

        if i >= len(lines) - 1:
            key, value = _get_key_value(current_line)
            # If this is some unparsable information, then
            # just skip it.
            if key:
                info[current_item][key] = value
            return info, i

        next_line = lines[i + 1]
        next_line_indentation = _get_indentation(next_line)

        if current_line_indentation == next_line_indentation:
            key, value = _get_key_value(current_line)
            if key:
                info[current_item][key] = value
            i = i + 1
        elif next_line_indentation > current_line_indentation:
            ret_dict, j = _get_dict(lines, i, current_line_indentation)
            info[current_item].update(ret_dict)
            i = j + 1
        elif next_line_indentation < current_line_indentation:
            key, value = _get_key_value(current_line)
            if key:
                info[current_item][key] = value
            return info, i

    return info, i


class HPRaidController(RaidController):
    def __init__(self, controller_name, data):
        self.controller_name = controller_name
        self.data = data

    def get_product_name(self):
        return self.controller_name

    def get_manufacturer(self):
        return 'HP'

    def get_serial_number(self):
        return self.data['Serial Number']

    def get_firmware_version(self):
        return self.data['Firmware Version']

    def get_physical_disks(self):
        ret = []
        output = subprocess.getoutput(
            'ssacli ctrl slot={slot} pd all show detail'.format(slot=self.data['Slot'])
        )
        lines = output.split('\n')
        lines = list(filter(None, lines))
        j = -1
        while j < len(lines):
            info_dict, j = _get_dict(lines, j + 1, 0)

        key = next(iter(info_dict))
        for array, physical_disk in info_dict[key].items():
            for _, pd_attr in physical_disk.items():
                model = pd_attr.get('Model', '').strip()
                vendor = None
                if model.startswith('HP'):
                    vendor = 'HP'
                elif len(model.split()) > 1:
                    vendor = get_vendor(model.split()[1])
                else:
                    vendor = get_vendor(model)

                ret.append({
                    'Model': model,
                    'Vendor': vendor,
                    'SN': pd_attr.get('Serial Number', '').strip(),
                    'Size': pd_attr.get('Size', '').strip(),
                    'Type': 'SSD' if pd_attr.get('Interface Type') == 'Solid State SATA'
                    else 'HDD',
                    '_src': self.__class__.__name__,
                })
        return ret


class HPRaid(Raid):
    def __init__(self):
        self.output = subprocess.getoutput('ssacli ctrl all show detail')
        self.controllers = []
        self.convert_to_dict()

    def convert_to_dict(self):
        lines = self.output.split('\n')
        lines = list(filter(None, lines))
        j = -1
        while j < len(lines):
            info_dict, j = _get_dict(lines, j + 1, 0)
            if len(info_dict.keys()):
                _product_name = list(info_dict.keys())[0]
                product_name = REGEXP_CONTROLLER_HP.search(_product_name)
                if product_name:
                    self.controllers.append(
                        HPRaidController(product_name.group(1), info_dict[_product_name])
                    )

    def get_controllers(self):
        return self.controllers